home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / SciAn / src / ScianTextBoxes.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  72KB  |  2,785 lines

  1. /*    ScianTextBoxes.c
  2.     Routines for text boxes
  3.  
  4.     J. Lyons
  5.     9/28/90
  6.  
  7.     3/26/91    added simple editing
  8.     3/28/91 added routines to set font, size, line spacing
  9.     3/29/91 added provision for different style text boxes
  10.     4/19/91 added one-line editing 
  11.     5/13/91 EMP moved an #ifdef FONTS4D around a fontHandle
  12.     5/29/91 EMP removed lib headers
  13.     7/17/91 added multiline editing
  14.     7/22/91 added incredibly fancy frame for adjustable text boxes
  15.     8/12/91 modified to use ScianFontSystem routines
  16.     8/16/91 modified to use Set/Get2DIntBounds
  17.     8/27/91 added ACTIVATED attribute
  18.     8/28/91 added CUT, COPY, PASTE methods
  19.     9/5/91    added TextHeight routine
  20.     9/6/91    added help
  21.     10/30/91 added shift-clicking to press method
  22.     11/1/91 added double-clicking to press method
  23.     11/4/91 added control window for textboxes (with EMP)
  24.     1/8/92    added indentation control
  25.     1/20/92    fixed linespace problem for large sizes
  26.     1/22/92    made indentation behavior more like tabs, sort of
  27.     4/9/92  EMP removed argument from NEWCTLWINDOW method
  28.     6/13/92 EMP changed method prototypes to declarations
  29.     8/30/92 EMP added opacity dependency and make method
  30.     12/3/92 changed Press routine per new selection scheme
  31.     12/8/92 added auto horiz scrolling to one-line text boxes
  32.     1/21/93 did undo
  33.     3/2/93  rewrote code for one-line textboxes to clean up auto-scrolling
  34. */
  35.  
  36. #include "Scian.h"
  37. #include "ScianStyle.h"
  38. #include "ScianTypes.h"
  39. #include "ScianFontSystem.h"
  40. #include "ScianWindows.h"
  41. #include "ScianArrays.h"
  42. #include "ScianLists.h"
  43. #include "ScianDraw.h"
  44. #include "ScianColors.h"
  45. #include "ScianIDs.h"
  46. #include "ScianErrors.h"
  47. #include "ScianEvents.h"
  48. #include "ScianScripts.h"
  49. #include "ScianMethods.h"
  50. #include "ScianDialogs.h"
  51. #include "ScianControls.h"
  52. #include "ScianHelp.h"
  53. #include "ScianTitleBoxes.h"
  54. #include "ScianTextBoxes.h"
  55. #include "ScianSliders.h"
  56. #include "ScianButtons.h"
  57. #include "ScianDrawings.h"
  58. #include "ScianSymbols.h"
  59. #include "ScianSnap.h"
  60.  
  61. /* #define NEWWAY */
  62.  
  63. #define MAXLINE        255
  64. #define MAXNLINES    20
  65. #define MAXTEXTSIZE    96
  66.  
  67. /* Method declarations, NOT prototypes */
  68. static ObjPtr EditTextBox();
  69. static ObjPtr DrawTextBox();
  70. static ObjPtr DrawOneLiner();
  71. static ObjPtr PressTextBox();
  72. static ObjPtr PressOneLiner();
  73. static ObjPtr ShowTextBoxControls();
  74. static ObjPtr TextBoxNotCurrent();
  75. static ObjPtr CutText();
  76. static ObjPtr CopyText();
  77. static ObjPtr PasteText();
  78. static ObjPtr MakeHelpString();
  79. static ObjPtr SetTBVal();
  80. static ObjPtr MakeTextBoxOpaque();
  81.  
  82. /* static function prototypes */
  83. #ifdef PROTO
  84.  
  85. static void NewText(ObjPtr);
  86. static char *NextLine(char *, int, char *);
  87.  
  88. #else
  89.  
  90. static void NewText();
  91. static char *NextLine();
  92.  
  93. #endif
  94.  
  95.  
  96.  
  97. /*********************************************************************** GLOBALS */
  98. ObjPtr textBoxClass;
  99. static char *textBuf; /* pointer to buffer for drawing and editing text */
  100. static int textBufSize; /* current size of the buffer */
  101.  
  102. static int indent;    /* current indent amount, in pixels */
  103.  
  104. #define TEXTBUFFERSIZE    1000
  105. #define TEXTBUFFERINCR    100
  106.  
  107. void InitTextBoxes()
  108. {
  109.     ObjPtr list;
  110.     
  111.     textBoxClass = NewObject(controlClass, 0);
  112.     AddToReferenceList(textBoxClass);
  113.     SetVar(textBoxClass, NAME, NewString("Text Box"));
  114.     SetVar(textBoxClass, TYPESTRING, NewString("text box"));
  115.     SetVar(textBoxClass, TEXTFONT, NewString("Helvetica"));
  116.     SetVar(textBoxClass, TEXTSIZE, NewInt(12));
  117.     SetMethod(textBoxClass, DRAW, DrawTextBox);
  118.     SetMethod(textBoxClass, SETVAL, SetTBVal);
  119.     SetMethod(textBoxClass, PRESS, PressTextBox);
  120.     SetMethod(textBoxClass, KEYDOWN, EditTextBox);
  121.     SetMethod(textBoxClass, CUT, CutText);
  122.     SetMethod(textBoxClass, COPY, CopyText);
  123.     SetMethod(textBoxClass, PASTE, PasteText);
  124.     SetMethod(textBoxClass, SELECTALL, SelectAll);
  125.     SetMethod(textBoxClass, PICKUP, (FuncTyp) 0);
  126.     SetMethod(textBoxClass, MAKE1HELPSTRING, MakeHelpString);
  127.     SetMethod(textBoxClass, NEWCTLWINDOW, ShowTextBoxControls);
  128.     SetMethod(textBoxClass, YOURENOTCURRENT, TextBoxNotCurrent);
  129.     SetMethod(textBoxClass, ALIGNLEFT, AlignLeft);
  130.     SetMethod(textBoxClass, ALIGNCENTER, AlignCenter);
  131.     SetMethod(textBoxClass, ALIGNRIGHT, AlignRight);
  132.     SetMethod(textBoxClass, SETTEXTSIZE, SetTextSizeMethod);
  133.     SetMethod(textBoxClass, SETTEXTFONT, SetTextFontMethod);
  134.  
  135.     /* stuff for undo to work */
  136.     list = NewList();
  137.     PrefixList(list, NewSymbol(BOUNDS));
  138.     PrefixList(list, NewSymbol(BGNSEL));
  139.     PrefixList(list, NewSymbol(ENDSEL));
  140.     PrefixList(list, NewSymbol(TEXTFONT));
  141.     PrefixList(list, NewSymbol(TEXTSIZE));
  142.     PrefixList(list, NewSymbol(LINESPACE));
  143.     PrefixList(list, NewSymbol(ALIGNMENT));
  144.     PrefixList(list, NewSymbol(COLOR));
  145.     PrefixList(list, NewSymbol(BACKGROUND));
  146.     PrefixList(list, NewSymbol(LASTKEY));
  147.     PrefixList(list, NewSymbol(VALUE));
  148.     SetVar(textBoxClass, SNAPVARS, list);
  149.  
  150.     /*EMP stuff for opacity to work*/
  151.     DeclareDependency(textBoxClass, OPAQUE, BACKGROUND);
  152.     SetMethod(textBoxClass, OPAQUE, MakeTextBoxOpaque);
  153.  
  154.     /* get a buffer for drawing and editing text */
  155.     textBuf = (char *) Alloc(textBufSize = TEXTBUFFERSIZE);
  156.     if (!textBuf)
  157.     {
  158.         OMErr();
  159.         textBufSize = 0;
  160.     }
  161. }
  162.  
  163. void KillTextBoxes()
  164. {
  165.     DeleteThing(textBoxClass);
  166.     if (textBuf) Free(textBuf);
  167. }
  168.  
  169. static ObjPtr MakeTextBoxOpaque(textBox)
  170. ObjPtr textBox;
  171. /*EMP - Makes a text box's OPAQUE variable*/
  172. {
  173.     SetVar(textBox, OPAQUE, GetVar(textBox, BACKGROUND) ? ObjTrue : ObjFalse);
  174.     return ObjTrue;
  175. }
  176.  
  177. #ifdef PROTO
  178. ObjPtr NewTextBox(int left, int right, int bottom , int top, 
  179.                     int style, char *name, char *text)
  180. #else
  181. ObjPtr NewTextBox(left, right, bottom, top, style, name, text)
  182. int left, right, bottom, top, style;
  183. char *name, *text;
  184. #endif
  185. {
  186.     ObjPtr newBox;
  187.  
  188.     if (left > right)
  189.     {
  190.         register int n;
  191.         n = left; left = right; right = n;
  192.     }
  193.     if (bottom > top)
  194.     {
  195.         register int n;
  196.         n = bottom; bottom = top; top = n;
  197.     }
  198.     newBox = NewObject(textBoxClass, 0);
  199.     Set2DIntBounds(newBox, left, right, bottom, top);
  200.     SetVar(newBox, CLASSID, NewInt(CLASS_TEXTBOX));
  201.     SetVar(newBox, ACTIVATED, NewInt(true));
  202.     SetVar(newBox, STYLE, NewInt(style));
  203.     SetVar(newBox, NAME, NewString(name));
  204.     if (style & WITH_PIT) SetVar(newBox, BACKGROUND, NewInt(TEXTBOXCOLOR));
  205.     SetVar(newBox, VALUE, NewString(text));
  206.     SetVar(newBox, CHANGED, ObjTrue); /* flag to line-break text before drawing first time */
  207.     if (style & EDITABLE)
  208.     {
  209.         ObjPtr theLen = NewInt(strlen(text));
  210.         SetVar(newBox, BGNSEL, theLen);
  211.         SetVar(newBox, ENDSEL, theLen);
  212.     }
  213.     if (style & ONE_LINE)
  214.     {
  215.         SetMethod(newBox, DRAW, DrawOneLiner);
  216.         SetMethod(newBox, PRESS, PressOneLiner);
  217.         SetVar(newBox, FORWARDVECTOR, ObjTrue); /* for auto-scroll */
  218.         SetVar(newBox, HOROFFSET, NewInt(0));
  219.     }
  220.     return newBox;
  221. }
  222.  
  223. static ObjPtr MakeHelpString(textBox, theClass)
  224. ObjPtr textBox, theClass;
  225. {
  226.     int style = GetInt(GetVar(textBox, STYLE));
  227.  
  228.     *textBuf = '\0';
  229.  
  230.     if (style & EDITABLE) strcpy(textBuf,"This is an editable text box. \
  231. Click the left mouse button anywhere in the text to place the insertion point \
  232. for adding new text. Drag through text to select it for editing. ");
  233.  
  234.     if (style & ADJUSTABLE) strcat(textBuf, "To change the size or location \
  235. of this text box, first click anywhere in the text to select it. A frame will \
  236. appear around the text with eight small handles. Drag any of the handles to change \
  237. the size of the text box. Drag the frame itself to reposition the text.");
  238.  
  239.     if (style & ONE_LINE) strcat(textBuf, "If all the text will not fit \
  240. within the box, it will automatically scroll as you add or delete text, drag \
  241. through text to select it, or use the arrow keys to move the insertion point.");
  242.  
  243.     SetVar(theClass, HELPSTRING, *textBuf ? NewString(textBuf) : NULLOBJ);
  244.     return ObjTrue;
  245. }
  246.  
  247. #ifdef PROTO
  248. void ActivateTextBox(ObjPtr textBox, Bool act)
  249. #else
  250. void ActivateTextBox(textBox, act)
  251. ObjPtr textBox;
  252. Bool act;
  253. #endif
  254. {
  255.     if (act) SetVar(textBox, ACTIVATED, ObjTrue);
  256.     else
  257.     {
  258.         SetVar(textBox, ACTIVATED, ObjFalse);
  259.         Select(textBox, false);
  260.     }
  261.     ImInvalid(textBox);
  262. }
  263.  
  264. static ObjPtr SetTBVal(textBox, theText)
  265. ObjPtr textBox, theText;
  266. {
  267.     Bool deferChange;
  268.  
  269.     if (IsString(theText)) SetTextBox(textBox, GetString(theText));
  270.     else if (IsInt(theText))
  271.     {
  272.         sprintf(tempStr, "%d", GetInt(theText));
  273.         SetTextBox(textBox, tempStr);
  274.     }
  275.     else if (IsReal(theText))
  276.     {
  277.         sprintf(tempStr, "%g", GetReal(theText));
  278.         SetTextBox(textBox, tempStr);
  279.     }
  280.     else return ObjFalse;
  281.  
  282.     return ObjTrue;
  283. }
  284.  
  285. /******************************************************************** SET TEXT ROUTINES */
  286. #ifdef PROTO
  287. ObjPtr SetTextBox(ObjPtr textBox, char *text)
  288. #else
  289. ObjPtr SetTextBox(textBox, text)
  290. ObjPtr textBox;
  291. char *text;
  292. #endif
  293. {
  294.     int style;
  295.     
  296.     style = GetInt(GetVar(textBox, STYLE));
  297.     SetVar(textBox, VALUE, NewString(text));
  298.     if (style & EDITABLE)
  299.     {
  300.         ObjPtr theLen = NewInt(strlen(text));
  301.         SetVar(textBox, BGNSEL, theLen);
  302.         SetVar(textBox, ENDSEL, theLen);
  303.     }
  304.     if (style & ONE_LINE)
  305.     {
  306.         SetVar(textBox, HOROFFSET, NewInt(0));
  307.         SetVar(textBox, FORWARDVECTOR, ObjTrue);
  308.     }
  309.     SetVar(textBox, CHANGED, ObjTrue); 
  310.     ImInvalid(textBox);
  311.     ChangedValue(textBox);
  312.     if (logging) LogControl(textBox);
  313.     return NULLOBJ;
  314. }
  315.  
  316. #ifdef PROTO
  317. ObjPtr SetTextFont(ObjPtr textBox, char *fontName)
  318. #else
  319. ObjPtr SetTextFont(textBox, fontName)
  320. ObjPtr textBox;
  321. char *fontName;
  322. #endif
  323. {
  324.     SetVar(textBox, TEXTFONT, NewString(fontName));
  325.     SetVar(textBox, CHANGED, ObjTrue); 
  326.     ImInvalid(textBox);
  327.     return NULLOBJ;
  328. }
  329.  
  330. #ifdef PROTO
  331. ObjPtr SetTextSize(ObjPtr textBox, int textSize)
  332. #else
  333. ObjPtr SetTextSize(textBox, textSize)
  334. ObjPtr textBox;
  335. int textSize;
  336. #endif
  337. {
  338.     if (textSize > 0 && textSize <= MAXTEXTSIZE)
  339.         SetVar(textBox, TEXTSIZE, NewInt(textSize));
  340.     SetVar(textBox, CHANGED, ObjTrue); 
  341.     ImInvalid(textBox);
  342.     return NULLOBJ;
  343. }
  344.  
  345. ObjPtr SetTextSizeMethod(textBox, textSize)
  346. ObjPtr textBox;
  347. ObjPtr textSize;
  348. /*Method for setting text size.  textSize is a string or integer or real.
  349.   Don't ask why.*/
  350. {
  351.     char *s;
  352.     int n;
  353.  
  354.     if (IsInt(textSize) || IsReal(textSize))
  355.     {
  356.     n = GetInt(textSize);
  357.     }
  358.     else if (IsString(textSize))
  359.     {
  360.     if (1 != sscanf(GetString(textSize), "%d", &n))
  361.     {
  362.         ReportError("SetTextSizeMethod", "String is not a number");
  363.         return ObjFalse;
  364.     }
  365.     }
  366.     else
  367.     {
  368.     ReportError("SetTextSizeMethod", "Value is not an integer or string");
  369.     return ObjFalse;
  370.     }
  371.  
  372.     SetTextSize(textBox, n);
  373.     return ObjTrue;
  374. }
  375.  
  376. ObjPtr SetTextFontMethod(textBox, textFont)
  377. ObjPtr textBox;
  378. ObjPtr textFont;
  379. /*Method for setting text font.  textFont is a STRING.*/
  380. {
  381.     char *s;
  382.     s = GetString(textFont);
  383.  
  384.     if (s)
  385.     {
  386.     SetTextFont(textBox, s);
  387.     }
  388.  
  389.     return ObjTrue;
  390. }
  391.  
  392. #ifdef PROTO
  393. ObjPtr SetTextLineSpace(ObjPtr textBox, int lineSpace)
  394. #else
  395. ObjPtr SetTextSize(textBox, lineSpace)
  396. ObjPtr textBox;
  397. int lineSpace;
  398. #endif
  399. {
  400.     if (lineSpace > 0)
  401.         SetVar(textBox, LINESPACE, NewInt(lineSpace));
  402.     ImInvalid(textBox);
  403.     return NULLOBJ;
  404. }
  405.  
  406. /*Methods for individual alignment*/
  407.  
  408. #ifdef PROTO
  409. ObjPtr SetTextAlign(ObjPtr textBox, int value)
  410. #else
  411. ObjPtr SetTextAlign(textBox, value)
  412. ObjPtr textBox;
  413. int value;
  414. #endif
  415. {
  416.     SetVar(textBox, ALIGNMENT, NewInt(value));
  417.     ImInvalid(textBox);
  418.     return NULLOBJ;
  419. }
  420.  
  421. ObjPtr AlignLeft(textBox)
  422. ObjPtr textBox;
  423. /*Aligns a text box left*/
  424. {
  425.     SetTextAlign(textBox, LEFTALIGN);
  426.     return ObjTrue;
  427. }
  428.  
  429. ObjPtr AlignCenter(textBox)
  430. ObjPtr textBox;
  431. /*Aligns a text box left*/
  432. {
  433.     SetTextAlign(textBox, CENTERALIGN);
  434.     return ObjTrue;
  435. }
  436.  
  437. ObjPtr AlignRight(textBox)
  438. ObjPtr textBox;
  439. /*Aligns a text box left*/
  440. {
  441.     SetTextAlign(textBox, RIGHTALIGN);
  442.     return ObjTrue;
  443. }
  444.  
  445. #ifdef PROTO
  446. ObjPtr SetTextColor(ObjPtr textBox, ObjPtr color)
  447. #else
  448. ObjPtr SetTextColor(textBox, color)
  449. ObjPtr textBox, color;
  450. #endif
  451. {
  452.     SetVar(textBox, COLOR, color);
  453.     ImInvalid(textBox);
  454.     return NULLOBJ;
  455. }
  456.  
  457. #ifdef PROTO
  458. ObjPtr SetTextBGColor(ObjPtr textBox, ObjPtr color)
  459. #else
  460. ObjPtr SetTextBGColor(textBox, color)
  461. ObjPtr textBox, color;
  462. #endif
  463. {
  464.     SetVar(textBox, BACKGROUND, color);
  465.     ImInvalid(textBox);
  466.     return NULLOBJ;
  467. }
  468.  
  469. #ifdef PROTO
  470. ObjPtr SetTextBoxStyle(ObjPtr textBox, int style)
  471. #else
  472. ObjPtr SetTextBoxStyle(textBox, style)
  473. ObjPtr textBox;
  474. int style;
  475. #endif
  476. {
  477.     SetVar(textBox, STYLE, NewInt(style));
  478.     if (style & ONE_LINE)
  479.     {
  480.         SetMethod(textBox, DRAW, DrawOneLiner);
  481.         SetMethod(textBox, PRESS, PressOneLiner);
  482.         SetVar(textBox, FORWARDVECTOR, ObjTrue); /* for auto-scroll */
  483.         SetVar(textBox, HOROFFSET, NewInt(0));
  484.         SetVar(textBox, FORWARDVECTOR, ObjTrue);
  485.     }
  486.     else
  487.     {
  488.         SetMethod(textBox, DRAW, DrawTextBox);
  489.         SetMethod(textBox, PRESS, PressTextBox);
  490.         SetVar(textBox, FORWARDVECTOR, NULLOBJ);
  491.         SetVar(textBox, HOROFFSET, NULLOBJ);
  492.         SetVar(textBox, FORWARDVECTOR, NULLOBJ);
  493.     }
  494.     SetVar(textBox, CHANGED, ObjTrue); 
  495.     ImInvalid(textBox);
  496.     return NULLOBJ;
  497. }
  498.  
  499. #ifdef NEWWAY
  500. /*********************************************************** NEW TEXT */
  501. #ifdef PROTO
  502. static void NewText(ObjPtr textBox)
  503. #else
  504. static void NewText(textBox)
  505. ObjPtr textBox;
  506. #endif
  507. {
  508.     /* puts "soft" eols (\r is used) into the text as needed */
  509.  
  510.     int left, right, bottom, top;
  511.     int textLeft, textRight;
  512.     ObjPtr theObj;
  513.     char *text, *nxtLine, *bgnLine, *s, *t, *fontName, lineBuf[MAXLINE + 1];
  514.     int len, width, size, style;
  515.  
  516.     Get2DIntBounds(textBox, &left, &right, &bottom, &top);
  517.  
  518.     MakeVar(textBox, VALUE);
  519.     text = GetString(GetVar(textBox, VALUE));
  520.  
  521.     theObj = GetVar(textBox, STYLE);
  522.     if (theObj) style = GetInt(theObj);
  523.     else style = PLAIN;
  524.  
  525.     if (style & ONE_LINE)
  526.     {
  527.         SetVar(textBox, VALUE, NewString(text));
  528.         return;
  529.     }
  530.  
  531.     theObj = GetVar(textBox, TEXTFONT);
  532.     if (theObj) fontName = GetString(theObj);
  533.     else fontName = DEFFONT;
  534.  
  535.     theObj = GetVar(textBox, TEXTSIZE);
  536.     if (theObj) size = GetInt(theObj);
  537.     else size = DEFTEXTSIZE;
  538.  
  539.     if (style & WITH_PIT)
  540.     {
  541.         textLeft = left + TEXTMARGIN;
  542.         textRight = right - TEXTMARGIN;
  543.     }
  544.     else if (style & (EDITABLE | ADJUSTABLE) || GetVar(textBox, BACKGROUND))
  545.     {
  546.         textLeft = left + HANDLESIZE + 2;
  547.         textRight = right - HANDLESIZE - 2;
  548.     }
  549.     else /* default is no inset */
  550.     {
  551.         textLeft = left;
  552.         textRight = right;
  553.     }
  554.  
  555.     /*** CHANGE FOR INDENTATION ***/
  556.     SetupFont(fontName, size);
  557.     width = textRight - textLeft;
  558.     nxtLine = text; /* set up for loop */
  559.     t = textBuf;
  560.     do {
  561.         if (t - textBuf + MAXLINE > textBufSize) /* increase buffer size */
  562.         {
  563.             int tIndex = t - textBuf; /* remember position */
  564.             textBuf = (char *) Realloc(textBuf, textBufSize + TEXTBUFFERINCR);
  565.             if (!textBuf) /* failed */
  566.             {
  567.                 OMErr();
  568.                 textBufSize = 0;
  569.                 return;
  570.             }
  571.             t = tIndex + textBuf; /* reset pointer in new buffer */
  572.         }
  573.         bgnLine = nxtLine;
  574.         nxtLine = NextLine(bgnLine, width, s=lineBuf); /* (ignores existing soft CRs) */
  575.         while (*t++ = *s++) ; /* copy line into textBuf */
  576.         --t; /* back up to terminator */
  577.         if (*nxtLine == '\n') ++nxtLine, *t++ = '\n';    /* paragraph */
  578.         else *t++ = '\r'; /* soft eol */
  579.     } while (*nxtLine);
  580.     *t = '\0'; /* stuff terminator */
  581.     SetVar(textBox, VALUE, NewString(textBuf));
  582.     SetVar(textBox, CHANGED, ObjFalse);
  583.     /* calling routine will handle draw, changed value */
  584. }
  585. #endif
  586.  
  587. /*********************************************************** TEXT HEIGHT */
  588. #ifdef PROTO
  589. int TextHeight(ObjPtr textBox)
  590. #else
  591. int TextHeight(textBox)
  592. ObjPtr textBox;
  593. #endif
  594. {
  595. #ifndef NEWWAY
  596.     int left, right, bottom, top;
  597.     int textLeft, textRight, textTop;
  598.     ObjPtr theObj;
  599.     char *text, *nxtLine, *bgnLine, lineBuf[MAXLINE + 1], *fontName;
  600.     int uiFont, y, width, size, align, lineSpace, style;
  601.     int tabWid;
  602.     Bool uiTB = false;
  603.  
  604.     Get2DIntBounds(textBox, &left, &right, &bottom, &top);
  605.  
  606.     MakeVar(textBox, VALUE);
  607.     theObj = GetVar(textBox, VALUE);
  608.     if (theObj) text = GetString(theObj);
  609.     else text = "\0";
  610.         
  611.     theObj = GetVar(textBox, STYLE);
  612.     if (theObj) style = GetInt(theObj);
  613.     else style = PLAIN;
  614.  
  615.     theObj = GetVar(textBox, UIFONT);
  616.     if (theObj)
  617.     {
  618.         uiFont = GetInt(theObj);
  619.         size = uiFontInfo[uiFont].size;
  620.         uiTB = true;
  621.     }
  622.     else
  623.     {
  624.         theObj = GetStringVar("TextHeight", textBox, TEXTFONT);
  625.         if (theObj) fontName = GetString(theObj);
  626.         else return 0;
  627.  
  628.         theObj = GetIntVar("TextHeight", textBox, TEXTSIZE);
  629.         if (theObj) size = GetInt(theObj);
  630.         else return 0;
  631.     }
  632.  
  633.     theObj = GetVar(textBox, ALIGNMENT);
  634.     if (theObj) align = GetInt(theObj);
  635.     else align = LEFTALIGN;
  636.  
  637.     theObj = GetVar(textBox, LINESPACE);
  638.     if (theObj) lineSpace = GetInt(theObj);
  639.     else lineSpace = 4 + size/3;
  640.  
  641.     MakeVar(textBox, BACKGROUND);
  642.  
  643.     if (style & WITH_PIT)
  644.     {
  645.         textLeft = left + TEXTMARGIN;
  646.         textRight = right - TEXTMARGIN;
  647.         textTop = top - TEXTMARGIN;
  648.     }
  649.     else if (style & (EDITABLE | ADJUSTABLE) || GetVar(textBox, BACKGROUND))
  650.     {
  651.         textLeft = left + HANDLESIZE + 2;
  652.         textRight = right - HANDLESIZE - 2;
  653.         textTop = top - HANDLESIZE - 2;
  654.     }
  655.     else /* default is no inset */
  656.     {
  657.         textLeft = left;
  658.         textRight = right;
  659.         textTop = top;
  660.     }
  661.  
  662.     if (uiTB)
  663.     {
  664.         SetUIFont(uiFont);
  665.     }
  666.     else
  667.     {
  668.         SetupFont(fontName, size);
  669.     }
  670.     tabWid = TABWID*ChrWidth('0');
  671.     y = textTop - size;
  672.     width = textRight - textLeft;
  673.     indent = 0;
  674.     bgnLine = nxtLine = text; /* set up for loop */
  675.     do {
  676.         bgnLine = nxtLine;
  677.         nxtLine = NextLine(bgnLine, width - indent, lineBuf);
  678.         y -= size + lineSpace;
  679.         if (*nxtLine == '\n')
  680.         {
  681.             ++nxtLine; /* paragraph */
  682.             indent = 0;
  683.         }
  684.         else indent += ChrCount(lineBuf, '\t')*tabWid;
  685.     } while (*nxtLine);
  686.     return textTop - y + size;
  687. #else
  688.     ObjPtr theObj;
  689.     int n, size, lineSpace;
  690.     char *t;
  691.         
  692.     if (GetPredicate(textBox, CHANGED)) NewText(textBox);
  693.  
  694.     MakeVar(textBox, VALUE);
  695.     t = GetString(GetVar(textBox, VALUE));
  696.  
  697.     theObj = GetVar(textBox, TEXTSIZE);
  698.     if (theObj) size = GetInt(theObj);
  699.     else size = DEFTEXTSIZE;
  700.  
  701.     theObj = GetVar(textBox, LINESPACE);
  702.     if (theObj) lineSpace = GetInt(theObj);
  703.     else lineSpace = 4 + size/3;
  704.  
  705.     n = 0;
  706.     while (*t)
  707.     {
  708.         if (*t == '\r' || *t == '\n') ++n;
  709.         ++t;
  710.     }
  711.     return n*(size + lineSpace);
  712. #endif
  713. }
  714.  
  715. int ChrCount(s, c)
  716. char *s, c;
  717. {
  718.     int n=0;
  719.     while (*s) if (*s++ == c) ++n;
  720.     return n;
  721. }
  722.  
  723. /************************************************************************** DRAW TEXT BOX */
  724. static ObjPtr DrawTextBox(textBox)
  725. ObjPtr textBox;
  726. {
  727. #ifdef GRAPHICS
  728.     Bool active;
  729.     int left, right, bottom, top;
  730.     int textLeft, textRight, textBottom, textTop;
  731.     int x,y;
  732.     ObjPtr theObj, color, bgColor;
  733.     char *s, *t, *text, *nxtLine, *bgnLine, lineBuf[MAXLINE + 1], *fontName;
  734.     int lineStart, selStart, selEnd; /* distances for line and selection */
  735.     int bgnSel, endSel; /* index to text selection */
  736.     int uiFont, align, length, width, center, size, lineSpace, style; 
  737.     int tabWid, nTabs;
  738.     Bool uiTB = false;
  739.  
  740.     Get2DIntBounds(textBox, &left, &right, &bottom, &top);
  741.     if (IsDrawingRestricted(left, right, bottom, top)) return ObjFalse;
  742.  
  743.     active = GetPredicate(textBox, ACTIVATED);
  744.     
  745.     MakeVar(textBox, VALUE);
  746.     theObj = GetStringVar("DrawTextBox", textBox, VALUE);
  747.     if (theObj) text = GetString(theObj);
  748.     else text = "\0";
  749.         
  750.     if ((length = strlen(text)) > textBufSize) /* grow buffer */
  751.     {
  752.         textBuf = (char *) Realloc(textBuf, length + TEXTBUFFERINCR);
  753.         if (!textBuf) /* failed */
  754.         {
  755.             OMErr();
  756.             textBufSize = 0;
  757.             return NULLOBJ;
  758.         }
  759.     }
  760.  
  761.     theObj = GetIntVar("DrawTextBox", textBox, STYLE);
  762.     if (theObj) style = GetInt(theObj);
  763.     else style = PLAIN;
  764.  
  765.     theObj = GetVar(textBox, ALIGNMENT);
  766.     if (theObj) align = GetInt(theObj);
  767.     else align = LEFTALIGN;
  768.  
  769.     theObj = GetVar(textBox, UIFONT);
  770.     if (theObj)
  771.     {
  772.         uiFont = GetInt(theObj);
  773.         size = uiFontInfo[uiFont].size;
  774.         uiTB = true;
  775.     }
  776.     else
  777.     {
  778.         theObj = GetStringVar("DrawTextBox", textBox, TEXTFONT);
  779.         if (theObj) fontName = GetString(theObj);
  780.         else return ObjFalse;
  781.  
  782.         theObj = GetIntVar("DrawTextBox", textBox, TEXTSIZE);
  783.         if (theObj) size = GetInt(theObj);
  784.         else return ObjFalse;
  785.     }
  786.  
  787.     theObj = GetVar(textBox, LINESPACE);
  788.     if (theObj) lineSpace = GetInt(theObj);
  789.     else lineSpace = 4 + size/3;
  790.  
  791.     MakeVar(textBox, COLOR);
  792.     color = GetVar(textBox, COLOR);
  793.  
  794.     MakeVar(textBox, BACKGROUND);
  795.     bgColor = GetVar(textBox, BACKGROUND);
  796.  
  797.     if (active && AmICurrent(textBox) && style & EDITABLE)
  798.     {
  799.         theObj = GetVar(textBox, BGNSEL);
  800.         if (theObj) bgnSel = GetInt(theObj);
  801.         else bgnSel = strlen(text);
  802.  
  803.         theObj = GetVar(textBox, ENDSEL);
  804.         if (theObj) endSel = GetInt(theObj);
  805.         else endSel = bgnSel;
  806.     }
  807.  
  808.     if (style & WITH_PIT)
  809.     {
  810.         DrawSunkenRect(left, right, bottom, top,
  811.             active ? TEXTBOXCOLOR : UIBACKGROUND);
  812.         textLeft = left + TEXTMARGIN;
  813.         textRight = right - TEXTMARGIN;
  814.         textBottom = bottom + TEXTMARGIN;
  815.         textTop = top - TEXTMARGIN;
  816.     }
  817.     else if (bgColor)
  818.     {
  819.         SetObjectColor(bgColor);
  820.         FillRect(left, right, bottom, top);
  821.         textLeft = left + HANDLESIZE + 2;
  822.         textRight = right - HANDLESIZE - 2;
  823.         textBottom = bottom + HANDLESIZE + 2;
  824.         textTop = top - HANDLESIZE - 2;
  825.     }
  826.     else if (style & (EDITABLE | ADJUSTABLE))
  827.     {
  828.         textLeft = left + HANDLESIZE + 2;
  829.         textRight = right - HANDLESIZE - 2;
  830.         textBottom = bottom + HANDLESIZE + 2;
  831.         textTop = top - HANDLESIZE - 2;
  832.     }
  833.     else /* default is no inset */
  834.     {
  835.         textLeft = left;
  836.         textRight = right;
  837.         textBottom = bottom;
  838.         textTop = top;
  839.     }
  840.     if (style & ADJUSTABLE) SetClipRect(left, right, bottom, top);
  841.     else SetClipRect(textLeft-1, textRight+1, textBottom-1, textTop+1);
  842.  
  843.     /* wake up, time to draw */
  844.     if (uiTB)
  845.     {
  846.         SetUIFont(uiFont);
  847.     }
  848.     else
  849.     {
  850.         SetupFont(fontName, size);
  851.     }
  852.     y = textTop - size;
  853.     width = textRight - textLeft;
  854.     tabWid = TABWID*ChrWidth('0');
  855.     indent = 0;
  856.     center = (textLeft + textRight)/2;
  857.     nxtLine = text; /* set up for loop */
  858.     do {
  859.         bgnLine = nxtLine;
  860.         nxtLine = NextLine(bgnLine, width - indent, lineBuf);
  861.         length = StrWidth(lineBuf);
  862.         switch (align)
  863.         {
  864.         case CENTERALIGN:
  865.                    lineStart = center - length/2;
  866.             break;
  867.  
  868.         case RIGHTALIGN:
  869.             lineStart = textRight - length;
  870.             break;
  871.  
  872.         case LEFTALIGN:
  873.         default:
  874.             lineStart = textLeft + indent;
  875.             break;
  876.         }
  877.         if (active && AmICurrent(textBox) && style & EDITABLE)
  878.         {
  879.             /* draw text cursor if in this line */
  880.             if (bgnSel == endSel && text + bgnSel <= nxtLine
  881.                 && text + bgnSel >= bgnLine)
  882.             {
  883.                 /* find position of insertion point */
  884.                 int i = bgnSel - (bgnLine - text);
  885.  
  886.                 strncpy(textBuf, bgnLine, i);
  887.                 textBuf[i] = '\0';
  888.                 selStart = StrWidth(textBuf);
  889.  
  890.                 /* draw text cursor before bgnSel */
  891.                 FillUIRect(lineStart + selStart, lineStart + selStart + 1,
  892.                     y - lineSpace + 2, y + size + 2, TEXTCURSORCOLOR);
  893.             }
  894.             else if (text + endSel > bgnLine && text + bgnSel < nxtLine)
  895.             {
  896.                 int selectColor;
  897.                 /* some of the selection is on this line */
  898.                 if (text + bgnSel < bgnLine) selStart = lineStart;
  899.                 else /* compute dist to start of selection */
  900.                 {
  901.                     int i = bgnSel - (bgnLine - text);
  902.  
  903.                     strncpy(textBuf, bgnLine, i);
  904.                     textBuf[i] = '\0';
  905.                     selStart = lineStart + StrWidth(textBuf);
  906.                 }
  907.                 if (text + endSel >= nxtLine) selEnd = lineStart + length;
  908.                 else /* compute dist to end of selection */
  909.                 {
  910.                     int i = endSel - (bgnLine - text);
  911.  
  912.                     strncpy(textBuf, bgnLine, i);
  913.                     textBuf[i] = '\0';
  914.                     selEnd = lineStart + StrWidth(textBuf);
  915.                 }
  916.                 /* now draw the selection rectangle for this line */
  917.                 selectColor = ChooseSelectionColor(textBox);
  918.                 FillUIRect(selStart, selEnd, y - lineSpace + 2, y + size + 2,
  919.                             selectColor);
  920.             }
  921.         }
  922.         /* now draw the text */
  923.         if (color) SetObjectColor(color);
  924.         else SetUIColor(UITEXT);
  925.         nTabs = 0; s = t = lineBuf; /* set up for loop */
  926.         while (true)
  927.         {
  928.             if (*t == '\0')    /* end of line */
  929.             {
  930.                 switch (align)
  931.                 {
  932.                     case CENTERALIGN:
  933.                     DrawAString(CENTERALIGN, center, y, s);
  934.                     break;
  935.                     
  936.                     case RIGHTALIGN:
  937.                     DrawAString(RIGHTALIGN, textRight, y, s);
  938.                     break;
  939.                     
  940.                     case LEFTALIGN:
  941.                     default:
  942.                     DrawAString(LEFTALIGN, lineStart, y, s);
  943.                     break;
  944.                 }
  945.                 break;
  946.             }
  947.             else if (*t == '\t') /* handle tab */
  948.             {
  949.                 *t = '\0'; /* stuff terminator for this segment */
  950.                 DrawAString(LEFTALIGN, lineStart, y, s);
  951.                 indent = (++nTabs)*tabWid;
  952.                 lineStart = textLeft + indent;
  953.                 s = ++t;
  954.                 continue;
  955.             }
  956.             else ++t;                        
  957.         }
  958.         if ((y -= size + lineSpace) < textBottom) break; /* whoa! past bottom of box */
  959.         if (*nxtLine == '\n')
  960.         {
  961.             ++nxtLine; /* paragraph */
  962.             indent = 0;
  963.         }
  964.     } while (*nxtLine);
  965.  
  966.     if (IsSelected(textBox) && style & ADJUSTABLE)
  967.     {
  968.         /* Draw incredibly fancy frame for moving and resizing text box */
  969.  
  970.         int horCent = (left + right)/2;
  971.         int vertCent = (bottom + top)/2;
  972. #if 0
  973.         FrameUIWideRect(left+INSET, right-INSET,
  974.                 bottom+INSET, top-INSET,
  975.                 OUTSIDEFRAMEWEIGHT, OUTSIDEFRAMECOLOR);
  976.         FrameUIWideRect(left+INSET+OUTSIDEFRAMEWEIGHT,
  977.                 right-INSET-OUTSIDEFRAMEWEIGHT,
  978.                 bottom+INSET+OUTSIDEFRAMEWEIGHT,
  979.                 top-INSET-OUTSIDEFRAMEWEIGHT,
  980.                 INSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
  981.         FrameUIWideRect(left+INSET+OUTSIDEFRAMEWEIGHT+INSIDEFRAMEWEIGHT,
  982.                 right-INSET-OUTSIDEFRAMEWEIGHT-INSIDEFRAMEWEIGHT,
  983.                 bottom+INSET+OUTSIDEFRAMEWEIGHT+INSIDEFRAMEWEIGHT,
  984.                 top-INSET-OUTSIDEFRAMEWEIGHT-INSIDEFRAMEWEIGHT,
  985.                 OUTSIDEFRAMEWEIGHT, OUTSIDEFRAMECOLOR);
  986. #else
  987.         FrameUIWideRect(left+INSET, right-INSET,
  988.                 bottom+INSET, top-INSET,
  989.                 2*OUTSIDEFRAMEWEIGHT + INSIDEFRAMEWEIGHT + 1,
  990.                 OUTSIDEFRAMECOLOR);
  991.         FrameUIWideRect(left+INSET+OUTSIDEFRAMEWEIGHT,
  992.                 right-INSET-OUTSIDEFRAMEWEIGHT,
  993.                 bottom+INSET+OUTSIDEFRAMEWEIGHT,
  994.                 top-INSET-OUTSIDEFRAMEWEIGHT,
  995.                 INSIDEFRAMEWEIGHT + 1, INSIDEFRAMECOLOR);
  996. #endif
  997.         /* Now draw the handles */
  998.         /* center of sides */
  999.         FillUIRect(left, left+HANDLESIZE,
  1000.                 vertCent-HANDLESIZE/2, vertCent+HANDLESIZE/2, OUTSIDEFRAMECOLOR);
  1001.         FillUIRect(left+OUTSIDEFRAMEWEIGHT,
  1002.                 left+HANDLESIZE-OUTSIDEFRAMEWEIGHT,
  1003.                 vertCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
  1004.                 vertCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
  1005.  
  1006.         FillUIRect(right-HANDLESIZE, right,
  1007.                 vertCent-HANDLESIZE/2, vertCent+HANDLESIZE/2, OUTSIDEFRAMECOLOR);
  1008.         FillUIRect(right-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
  1009.                 right-OUTSIDEFRAMEWEIGHT,
  1010.                 vertCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
  1011.                 vertCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
  1012.  
  1013.         /* top edge */
  1014.         FillUIRect(horCent-HANDLESIZE/2, horCent+HANDLESIZE/2,
  1015.                 top-HANDLESIZE, top, OUTSIDEFRAMECOLOR);
  1016.         FillUIRect(horCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
  1017.                 horCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT,
  1018.                 top-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
  1019.                 top-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
  1020.  
  1021.         FillUIRect(left, left+HANDLESIZE,
  1022.                 top-HANDLESIZE, top, OUTSIDEFRAMECOLOR);
  1023.         FillUIRect(left+OUTSIDEFRAMEWEIGHT,
  1024.                 left+HANDLESIZE-OUTSIDEFRAMEWEIGHT,
  1025.                 top-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
  1026.                 top-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
  1027.         
  1028.         FillUIRect(right-HANDLESIZE, right,
  1029.                 top-HANDLESIZE, top, OUTSIDEFRAMECOLOR);
  1030.         FillUIRect(right-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
  1031.                 right-OUTSIDEFRAMEWEIGHT,
  1032.                 top-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
  1033.                 top-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
  1034.  
  1035.         /* bottom edge */
  1036.         FillUIRect(horCent-HANDLESIZE/2, horCent+HANDLESIZE/2,
  1037.                 bottom, bottom+HANDLESIZE, OUTSIDEFRAMECOLOR);
  1038.         FillUIRect(horCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
  1039.                 horCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT,
  1040.                 bottom+OUTSIDEFRAMEWEIGHT,
  1041.                 bottom+HANDLESIZE-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
  1042.  
  1043.         FillUIRect(left, left+HANDLESIZE,
  1044.                 bottom, bottom+HANDLESIZE, OUTSIDEFRAMECOLOR);
  1045.         FillUIRect(left+OUTSIDEFRAMEWEIGHT,
  1046.                 left+HANDLESIZE-OUTSIDEFRAMEWEIGHT,
  1047.                 bottom+OUTSIDEFRAMEWEIGHT,
  1048.                 bottom+HANDLESIZE-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
  1049.         
  1050.         FillUIRect(right-HANDLESIZE, right,
  1051.                 bottom, bottom+HANDLESIZE, OUTSIDEFRAMECOLOR);
  1052.         FillUIRect(right-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
  1053.                 right-OUTSIDEFRAMEWEIGHT,
  1054.                 bottom+OUTSIDEFRAMEWEIGHT,
  1055.                 bottom+HANDLESIZE-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
  1056.     }
  1057.     RestoreClipRect();
  1058. #endif
  1059.     return ObjTrue;
  1060. }
  1061.  
  1062. static ObjPtr DrawOneLiner(textBox)
  1063. ObjPtr textBox;
  1064. {
  1065. #ifdef GRAPHICS
  1066.     Bool active, showEnd;
  1067.     int left, right, bottom, top;
  1068.     int textLeft, textRight, textBottom, textTop;
  1069.     ObjPtr theObj, color, bgColor;
  1070.     char *fontName, *text;
  1071.     int lineStart, selStart, selEnd; /* distances for line and selection */
  1072.     int bgnSel, endSel; /* index to text selection */
  1073.     int uiFont, align, length, center, size, style, horOffset; 
  1074.     Bool uiTB = false;
  1075.  
  1076.     Get2DIntBounds(textBox, &left, &right, &bottom, &top);
  1077.     if (IsDrawingRestricted(left, right, bottom, top)) return ObjFalse;
  1078.  
  1079.     active = GetPredicate(textBox, ACTIVATED);
  1080.     
  1081.     MakeVar(textBox, VALUE);
  1082.     theObj = GetStringVar("DrawTextBox", textBox, VALUE);
  1083.     if (theObj) text = GetString(theObj);
  1084.     else text = "\0";
  1085.         
  1086.     theObj = GetIntVar("DrawTextBox", textBox, STYLE);
  1087.     if (theObj) style = GetInt(theObj);
  1088.     else style = PLAIN;
  1089.  
  1090.     theObj = GetVar(textBox, ALIGNMENT);
  1091.     if (theObj) align = GetInt(theObj);
  1092.     else align = LEFTALIGN;
  1093.  
  1094.     theObj = GetVar(textBox, UIFONT);
  1095.     if (theObj)
  1096.     {
  1097.         uiFont = GetInt(theObj);
  1098.         size = uiFontInfo[uiFont].size;
  1099.         uiTB = true;
  1100.     }
  1101.     else
  1102.     {
  1103.         theObj = GetStringVar("DrawTextBox", textBox, TEXTFONT);
  1104.         if (theObj) fontName = GetString(theObj);
  1105.         else return ObjFalse;
  1106.  
  1107.         theObj = GetIntVar("DrawTextBox", textBox, TEXTSIZE);
  1108.         if (theObj) size = GetInt(theObj);
  1109.         else return ObjFalse;
  1110.     }
  1111.  
  1112.     theObj = GetVar(textBox, HOROFFSET);
  1113.     if (theObj) horOffset = GetInt(theObj);
  1114.     else horOffset = 0;
  1115.  
  1116.     MakeVar(textBox, COLOR);
  1117.     color = GetVar(textBox, COLOR);
  1118.  
  1119.     MakeVar(textBox, BACKGROUND);
  1120.     bgColor = GetVar(textBox, BACKGROUND);
  1121.  
  1122.     if (active && AmICurrent(textBox) && style & EDITABLE)
  1123.     {
  1124.         theObj = GetVar(textBox, BGNSEL);
  1125.         if (theObj) bgnSel = GetInt(theObj);
  1126.         else bgnSel = strlen(text);
  1127.  
  1128.         theObj = GetVar(textBox, ENDSEL);
  1129.         if (theObj) endSel = GetInt(theObj);
  1130.         else endSel = bgnSel;
  1131.     }
  1132.  
  1133.     if (style & WITH_PIT)
  1134.     {
  1135.         DrawSunkenRect(left, right, bottom, top,
  1136.             active ? TEXTBOXCOLOR : UIBACKGROUND);
  1137.         textLeft = left + TEXTMARGIN;
  1138.         textRight = right - TEXTMARGIN;
  1139.         textBottom = bottom + TEXTMARGIN;
  1140.         textTop = top - TEXTMARGIN;
  1141.     }
  1142.     else if (bgColor)
  1143.     {
  1144.         SetObjectColor(bgColor);
  1145.         FillRect(left, right, bottom, top);
  1146.         textLeft = left + HANDLESIZE + 2;
  1147.         textRight = right - HANDLESIZE - 2;
  1148.         textBottom = bottom + HANDLESIZE + 2;
  1149.         textTop = top - HANDLESIZE - 2;
  1150.     }
  1151.     else /* default is no inset */
  1152.     {
  1153.         textLeft = left;
  1154.         textRight = right;
  1155.         textBottom = bottom;
  1156.         textTop = top;
  1157.     }
  1158.     SetClipRect(textLeft-1, textRight+1, textBottom-1, textTop+1);
  1159.     
  1160.     if (uiTB)
  1161.     {
  1162.         SetUIFont(uiFont);
  1163.     }
  1164.     else
  1165.     {
  1166.         SetupFont(fontName, size);
  1167.     }
  1168.     length = StrWidth(text);
  1169.     center = (textLeft + textRight)/2;
  1170.     switch (align)
  1171.     {
  1172.         case CENTERALIGN:
  1173.                    lineStart = center - length/2;
  1174.             break;
  1175.  
  1176.         case RIGHTALIGN:
  1177.             lineStart = textRight - length;
  1178.             break;
  1179.  
  1180.         case LEFTALIGN:
  1181.         default:
  1182.             lineStart = textLeft;
  1183.             break;
  1184.     }
  1185.     if (active && AmICurrent(textBox) && style & EDITABLE)
  1186.     {
  1187.         /* find co-ords of beginning and end of selection (without offset) */    
  1188.         strncpy(textBuf, text, bgnSel);
  1189.         textBuf[bgnSel] = '\0';
  1190.         selStart = lineStart + StrWidth(textBuf);
  1191.         if (bgnSel == endSel)
  1192.         {
  1193.         selEnd = selStart + 1;
  1194.         }
  1195.         else
  1196.         {
  1197.         int n = endSel - bgnSel;
  1198.         
  1199.         strncpy(textBuf, text + bgnSel, n);
  1200.         textBuf[n] = '\0';
  1201.         selEnd = selStart + StrWidth(textBuf);
  1202.         }
  1203.         
  1204.         /* check horizontal offset, update if necessary */
  1205.         showEnd = GetPredicate(textBox, FORWARDVECTOR);
  1206.         /* NOTE: This variable is set in the PRESS and EDIT methods to 
  1207.          * indicate which end of the selection to show when auto-scrolling.
  1208.          */
  1209.         if (showEnd) /* adjust offset to show the end of selection */
  1210.         {
  1211.         if (selEnd - horOffset < textLeft)
  1212.         {
  1213.             horOffset = selEnd - textLeft - 2;
  1214.             theObj = NewInt(horOffset);
  1215.             SetVar(textBox, HOROFFSET, theObj);
  1216.         }
  1217.         else if (selEnd - horOffset > textRight)
  1218.         {
  1219.             horOffset = selEnd - textRight + 2;
  1220.             theObj = NewInt(horOffset);
  1221.             SetVar(textBox, HOROFFSET, theObj);
  1222.         }
  1223.         }
  1224.         else /* adust offset to show beginning of selection */
  1225.         {
  1226.         if (selStart - horOffset < textLeft)
  1227.         {
  1228.             horOffset = selStart - textLeft - 2;
  1229.             theObj = NewInt(horOffset);
  1230.             SetVar(textBox, HOROFFSET, theObj);
  1231.         }
  1232.         else if (selStart - horOffset > textRight)
  1233.         {
  1234.             horOffset = selStart - textRight + 2;
  1235.             theObj = NewInt(horOffset);
  1236.             SetVar(textBox, HOROFFSET, theObj);
  1237.         }
  1238.         }
  1239.         /* draw text cursor or selection rectangle */
  1240.         FillUIRect(selStart - horOffset, selEnd - horOffset,
  1241.             textBottom, textTop, ChooseSelectionColor(textBox));
  1242.     }
  1243.     /* now draw the text */
  1244.     if (color) SetObjectColor(color);
  1245.     else SetUIColor(UITEXT);
  1246.     switch (align)
  1247.     {
  1248.         case CENTERALIGN:
  1249.         DrawAString(CENTERALIGN, center - horOffset, textTop - size, text);
  1250.         break;
  1251.         
  1252.         case RIGHTALIGN:
  1253.         DrawAString(RIGHTALIGN, textRight - horOffset, textTop - size, text);
  1254.         break;
  1255.         
  1256.         case LEFTALIGN:
  1257.         default:
  1258.         DrawAString(LEFTALIGN, lineStart - horOffset, textTop - size, text);
  1259.         break;
  1260.     }
  1261.     RestoreClipRect();
  1262. #endif
  1263.     return ObjTrue;
  1264. }
  1265.  
  1266. static ObjPtr PressTextBox(textBox, mouseX, mouseY, flags)
  1267. ObjPtr textBox;
  1268. int mouseX, mouseY;
  1269. long flags;
  1270. {
  1271. #ifdef INTERACTIVE
  1272.     ObjPtr theObj;
  1273.     char *text, *t, *fontName, lineBuf[MAXLINE + 1];
  1274.     int i, j, cj, l, n, width, length;
  1275.     int uiFont, style, align, size, nLines, lineSpace;
  1276.     Bool uiTB = false;
  1277.     float x;
  1278.     int y, mX, mY, offset[MAXNLINES * MAXLINE], lnx[3*(MAXNLINES + 1)];
  1279.     int left, right, bottom, top, hCent, vCent;
  1280.     int textLeft, textRight, textBot, textTop;
  1281.     int tabWid, nTabs;
  1282.  
  1283.     Get2DIntBounds(textBox, &left, &right, &bottom, &top);
  1284.  
  1285.     /* return if mouse outside text box */
  1286.     if (mouseX < left || mouseX > right || mouseY < bottom 
  1287.             || mouseY > top) return ObjFalse;
  1288.     
  1289.     /* get text parameters and scan text */
  1290.  
  1291.     theObj = GetIntVar("PressTextBox", textBox, STYLE);
  1292.     if (!theObj) style = PLAIN;
  1293.     else style = GetInt(theObj);
  1294.  
  1295.     MakeVar(textBox, VALUE);
  1296.     theObj = GetVar(textBox, VALUE);
  1297.     if (theObj) text = GetString(theObj);
  1298.     else text = "\0";
  1299.  
  1300.     theObj = GetVar(textBox, ALIGNMENT);
  1301.     if (theObj) align = GetInt(theObj);
  1302.     else align = LEFTALIGN;
  1303.  
  1304.     theObj = GetVar(textBox, UIFONT);
  1305.     if (theObj)
  1306.     {
  1307.     uiFont = GetInt(theObj);
  1308.     size = uiFontInfo[uiFont].size;
  1309.     uiTB = true;
  1310.     }
  1311.     else
  1312.     {
  1313.         theObj = GetStringVar("PressTextBox", textBox, TEXTFONT);
  1314.         if (theObj) fontName = GetString(theObj);
  1315.         else return ObjFalse;
  1316.  
  1317.         theObj = GetIntVar("PressTextBox", textBox, TEXTSIZE);
  1318.         if (theObj) size = GetInt(theObj);
  1319.         else return ObjFalse;
  1320.     }
  1321.  
  1322.     theObj = GetVar(textBox, LINESPACE);
  1323.     if (theObj) lineSpace = GetInt(theObj);
  1324.     else lineSpace = 4 + size/3;
  1325.     
  1326.     MakeVar(textBox, BACKGROUND);
  1327.  
  1328.     /* adjust bounds for background or pit */
  1329.     if (style & WITH_PIT)
  1330.     {
  1331.     textLeft = left + TEXTMARGIN;
  1332.     textRight = right - TEXTMARGIN;
  1333.     textBot = bottom + TEXTMARGIN;
  1334.     textTop = top - TEXTMARGIN;
  1335.     }
  1336.     else if (style & ADJUSTABLE || GetVar(textBox, BACKGROUND))
  1337.     {
  1338.     textLeft = left + (HANDLESIZE + 2);
  1339.     textRight = right - (HANDLESIZE + 2);
  1340.     textBot = bottom + (HANDLESIZE + 2);
  1341.     textTop = top - (HANDLESIZE + 2);
  1342.     }
  1343.     else
  1344.     {
  1345.     textLeft = left;
  1346.     textRight = right;
  1347.     textBot = bottom;
  1348.     textTop = top;
  1349.     }
  1350.     width = textRight - textLeft;
  1351.  
  1352.     if (uiTB)
  1353.     {
  1354.     SetUIFont(uiFont);
  1355.     }
  1356.     else
  1357.     {
  1358.     SetupFont(fontName, size);
  1359.     }
  1360.     tabWid = TABWID*ChrWidth('0');
  1361. /*
  1362. *    Build two arrays to keep character positions of text.
  1363. *    lnx[l] is the index of the first character of line l in text.
  1364. *    offset[i] is the total horiz offset to the middle of character i in text.
  1365. */
  1366.     t = text; l = 0; nLines = 0; lnx[0] = 0; nTabs = 0; indent = 0; /* setup for loop */
  1367.     do {
  1368.     t = NextLine(t, width - indent, lineBuf);
  1369.     lnx[++l] = t - text; /* index to first char of next line */
  1370.     length = StrWidth(lineBuf);
  1371.         
  1372.     /* figure out where line begins */
  1373.     switch (align)
  1374.     {
  1375.     case CENTERALIGN:
  1376.         x = (textLeft + textRight)/2 - length/2;
  1377.         break;
  1378.  
  1379.     case RIGHTALIGN:
  1380.         x = textRight - length;
  1381.         break;
  1382.  
  1383.     case LEFTALIGN:
  1384.     default:
  1385.         x = textLeft + indent;
  1386.         break;
  1387.     }
  1388.  
  1389.     /* build next line of char offsets */
  1390.     for (i=lnx[l-1]; i<lnx[l]; ++i)
  1391.     {
  1392.         register int w;
  1393.         if (text[i] == '\t')
  1394.         {
  1395.             w = x;
  1396.             x = textLeft + ( indent = (++nTabs)*tabWid );
  1397.             offset[i] = (x + w)/2;
  1398.         }
  1399.         else
  1400.         {
  1401.             w = ChrWidth(text[i]);
  1402.             offset[i] = x + w/2;
  1403.             x += w;
  1404.         }
  1405.     }
  1406.     if (*t == '\n')
  1407.     {
  1408.         ++t; /* paragraph */
  1409.         indent = 0;
  1410.         nTabs = 0;
  1411.     }
  1412.     if (l > MAXNLINES) break;
  1413.     } while (*t);
  1414.  
  1415.     nLines = l; /* number of lines of text */
  1416.  
  1417.     if (!IsSelected(textBox) && !GetVar(textBox, BACKGROUND))
  1418.     {
  1419.     /* see if the click is actually on text; if not, no hit */
  1420.     l = (textTop - mouseY)/(size + lineSpace);
  1421.     if (l < 0 || l >= nLines)
  1422.     {
  1423.         return ObjFalse; /* above or below text */
  1424.     }
  1425.     if (mouseX < offset[lnx[l]] - ChrWidth(text[lnx[l]])
  1426.         || mouseX > offset[lnx[l+1]-1])
  1427.     {
  1428.         return ObjFalse; /* to left or right of text */
  1429.     }
  1430.     }
  1431.  
  1432.     if (TOOL(flags) == T_HELP) /* help mode? */
  1433.     {
  1434.     ContextHelp(textBox);
  1435.     return ObjTrue;
  1436.     }
  1437.     
  1438.     /* return if not active */
  1439.     if (!GetPredicate(textBox, ACTIVATED)) return ObjTrue;
  1440.  
  1441.     /* return if not editable or adjustable */
  1442.     if (!(style & (EDITABLE | ADJUSTABLE))) return ObjTrue;
  1443.     
  1444.     if (style & ADJUSTABLE)    /* only adjustable TBs affect object selection */
  1445.     {
  1446.     if (!(flags & F_EXTEND) && !IsSelected(textBox))
  1447.     {
  1448.         /* new selection not already selected. Deselect the rest */
  1449.         DeselectAll();
  1450.     }
  1451.  
  1452.     if ((flags & F_EXTEND) && IsSelected(textBox) && !AmICurrent(textBox))
  1453.     {
  1454.         /*Deselect*/
  1455.         Select(textBox, false);
  1456.         return ObjTrue;
  1457.     }
  1458.     else if (!IsSelected(textBox))
  1459.     {
  1460.         /*Must select it*/
  1461.         Select(textBox, true);
  1462.     }
  1463.     }
  1464.     MakeMeCurrent(textBox);
  1465.     DrawMe(textBox);
  1466.  
  1467.     hCent = (left + right)/2;
  1468.     vCent = (bottom + top)/2;
  1469.  
  1470.     if (style & ADJUSTABLE) /* see if click is in frame */
  1471.     {
  1472.     Bool ml, mr, mb, mt;
  1473.     ml = mr = mb = mt = false;
  1474.  
  1475.     if (mouseX < left + HANDLESIZE) /* on left side */
  1476.     {
  1477.         if (mouseY > top - HANDLESIZE) /* top-left handle */
  1478.             mt = ml = true;
  1479.         else if (mouseY < bottom + HANDLESIZE) /* bottom-left handle */
  1480.             mb = ml = true;
  1481.         else if (mouseY > vCent - HANDLESIZE/2 && mouseY < vCent + HANDLESIZE/2)
  1482.             ml = true; /* middle-left handle */
  1483.         else ml = mr = mb = mt = true; /* in frame */
  1484.     }
  1485.     else if (mouseX > right - HANDLESIZE) /* on right side */
  1486.     {
  1487.         if (mouseY > top - HANDLESIZE) /* top-right handle */
  1488.             mt = mr = true;
  1489.         else if (mouseY < bottom + HANDLESIZE) /* bottom-right handle */
  1490.             mb = mr = true;
  1491.         else if (mouseY > vCent - HANDLESIZE/2 && mouseY < vCent + HANDLESIZE/2)
  1492.             mr = true; /* middle-right handle */
  1493.         else ml = mr = mb = mt = true; /* in frame */
  1494.     }
  1495.     else if (mouseY < bottom + HANDLESIZE) /* on bottom */
  1496.     {
  1497.         /* already handled (heh heh) corners */
  1498.         if (mouseX > hCent - HANDLESIZE/2 && mouseX < hCent + HANDLESIZE/2)
  1499.             mb = true; /* bottom middle handle */
  1500.         else ml = mr = mb = mt = true; /* in frame */
  1501.     }
  1502.     else if (mouseY > top - HANDLESIZE) /* on top */
  1503.     {
  1504.         /* already handled (heh heh) corners */
  1505.         if (mouseX > hCent - HANDLESIZE/2 && mouseX < hCent + HANDLESIZE/2)
  1506.             mt = true; /* middle top handle */
  1507.         else ml = mr = mb = mt = true; /* in frame */
  1508.     }
  1509.     else /* not on incredibly fancy frame */
  1510.     {
  1511.         if (!(style & EDITABLE)) return ObjTrue;
  1512.     }
  1513.     SaveForUndo(textBox);
  1514.     if (mr || ml || mb || mt) /* drag the incredibly fancy frame around */
  1515.     {
  1516.         /* I am greatly indebted to my friend and colleague, Eric Pepke,
  1517.            for the following code. Any errors or obfuscations are his. */
  1518.         int initX = mouseX, initY = mouseY;
  1519.         int newLeft, newRight, newBottom, newTop;
  1520.         int oldNewLeft, oldNewRight, oldNewBottom, oldNewTop;
  1521.  
  1522.         newLeft = oldNewLeft = left;
  1523.         newRight = oldNewRight = right;
  1524.         newBottom = oldNewBottom = bottom;
  1525.         newTop = oldNewTop = top;
  1526.  
  1527.         while (Mouse(&mX, &mY) && (mX == initX) && (mY == initY));
  1528.  
  1529.         DrawSkeleton(true);
  1530.  
  1531.         while (Mouse(&mX, &mY))
  1532.         {
  1533.         if (ml) newLeft = left + mX - initX;
  1534.         if (mr) newRight = right + mX - initX;
  1535.         if (mb) newBottom = bottom + mY - initY;
  1536.         if (mt) newTop = top + mY - initY;
  1537.  
  1538.         if (flags & F_CONSTRAIN)
  1539.         {
  1540.             /*Grid drag*/
  1541.             if (ml && mr && mb && mt)
  1542.             {
  1543.             /*Special case--whole object gridded
  1544.               Only grid top left*/
  1545.             int width, height;
  1546.             width = newRight - newLeft;
  1547.             height = newTop - newBottom;
  1548.             newLeft = GRIDX(newLeft);
  1549.             newRight = newLeft + width;
  1550.             newTop = top - (GRIDY(top - newTop));
  1551.             newBottom = newTop - height;
  1552.             }
  1553.             else
  1554.             {
  1555.             /*Normal case*/
  1556.             if (ml) newLeft = GRIDX(newLeft);
  1557.             if (mr) newRight = right - GRIDX(right - newRight);
  1558.             if (mb) newBottom = GRIDY(newBottom);
  1559.             if (mt) newTop = top - GRIDY(top - newTop);
  1560.             }
  1561.         }
  1562.         if (ml && newLeft + 3 * HANDLESIZE > newRight)
  1563.                 newLeft = newRight - 3 * HANDLESIZE;
  1564.         if (mr && newLeft + 3 * HANDLESIZE > newRight)
  1565.                 newRight = newLeft + 3 * HANDLESIZE;
  1566.         if (mb && newBottom + 3 * HANDLESIZE > newTop)
  1567.                 newBottom = newTop - 3 * HANDLESIZE;
  1568.         if (mt && newBottom + 3 * HANDLESIZE > newTop)
  1569.                 newTop = newBottom + 3 * HANDLESIZE;
  1570.         if ((newLeft != oldNewLeft ||
  1571.                 newRight != oldNewRight ||
  1572.                 newBottom != oldNewBottom ||
  1573.                 newTop != oldNewTop) &&
  1574.                 newLeft < newRight &&
  1575.                 newBottom < newTop)
  1576.         {
  1577.             Set2DIntBounds(textBox,
  1578.                 newLeft, newRight, newBottom, newTop);
  1579.             oldNewLeft = newLeft;
  1580.             oldNewRight = newRight;
  1581.             oldNewBottom = newBottom;
  1582.             oldNewTop = newTop;
  1583.             DrawMe(textBox);
  1584.         }
  1585.         }
  1586.         DrawSkeleton(false);
  1587.         if (logging)
  1588.         {
  1589.             char cmd[256];
  1590.             MakeObjectName(tempStr, textBox);
  1591.             sprintf(cmd, "set bounds %s [%d %d %d %d]\n",
  1592.                 tempStr, newLeft, newRight,
  1593.                 newBottom, newTop);
  1594.             Log(cmd);
  1595.         }
  1596.         return ObjTrue;
  1597.     }
  1598.     }
  1599.  
  1600.     /* click is in text. First reset LASTKEY to 'no previous character' state */
  1601.     SetVar(textBox, LASTKEY, NewInt(-1));
  1602.     /* SaveForUndo(textBox); */
  1603.     
  1604.     if (flags & F_EXTEND)
  1605.     {
  1606.     i = GetInt(GetVar(textBox, GetPredicate(textBox, FORWARDVECTOR) ? BGNSEL : ENDSEL));
  1607.     }
  1608.     else
  1609.     {
  1610.     /* find line and character position of mouse down */
  1611.     l = (textTop - mouseY)/(size + lineSpace);
  1612.     if (l < 0) i = 0;
  1613.     else if (l >= nLines)
  1614.     {
  1615.         i = lnx[nLines];
  1616.     }
  1617.     else
  1618.     {
  1619.         i=lnx[l];
  1620.         while (offset[i] < mouseX && i<lnx[l+1]) ++i;
  1621.     }
  1622.     }
  1623.  
  1624.     /* now track mouse and find line and char pos of end of selection */
  1625.     /* auto-scroll horizontally if one-line text box */
  1626.     mX = mouseX; mY = mouseY; /* setup for at least one loop */
  1627.     cj = -1;    /* force redraw first time */
  1628.     do {
  1629.     l = (textTop - mY)/(size + lineSpace);
  1630.     if (l < 0) j = 0;
  1631.     else if (l >= nLines)
  1632.     {
  1633.         j = lnx[nLines];
  1634.     }
  1635.     else 
  1636.     {
  1637.         j=lnx[l];
  1638.         while (offset[j] < mX && j<lnx[l+1]) ++j;
  1639.     }
  1640.     if (j != cj)    /* selection has changed */
  1641.     {
  1642.         if (i > j) /* make sure bgnSel < endSel */
  1643.         {
  1644.             SetVar(textBox, BGNSEL, NewInt(j));
  1645.             SetVar(textBox, ENDSEL, NewInt(i));
  1646.             SetVar(textBox, FORWARDVECTOR, ObjFalse);
  1647.         }
  1648.         else
  1649.         {
  1650.             SetVar(textBox, BGNSEL, NewInt(i));
  1651.             SetVar(textBox, ENDSEL, NewInt(j));
  1652.             SetVar(textBox, FORWARDVECTOR, ObjTrue);
  1653.         }
  1654.         DrawMe(textBox);
  1655.         cj = j;    /* remember current position */
  1656.     }
  1657.     } while (Mouse(&mX, &mY));
  1658.  
  1659.     if (i == j && flags & F_DOUBLECLICK)
  1660.     {
  1661.     /* select the word at insPt */
  1662.     while (i > 0)
  1663.     {
  1664.         if (IsAlphaNum(text[i-1])) --i;
  1665.         else break;
  1666.     }
  1667.     while (text[j] != '\0')
  1668.     {
  1669.         if (IsAlphaNum(text[j])) ++j;
  1670.         else break;
  1671.     }
  1672.     SetVar(textBox, BGNSEL, NewInt(i));
  1673.     SetVar(textBox, ENDSEL, NewInt(j));
  1674.     }
  1675. #endif
  1676.     return ObjTrue;
  1677. }
  1678.  
  1679. static ObjPtr PressOneLiner(textBox, mouseX, mouseY, flags)
  1680. ObjPtr textBox;
  1681. int mouseX, mouseY;
  1682. long flags;
  1683. {
  1684. #ifdef INTERACTIVE
  1685.     ObjPtr theObj;
  1686.     char *text, *fontName;
  1687.     int i, j, cj, length, horOffset, w;
  1688.     int uiFont, style, align, size, offset[MAXLINE];
  1689.     Bool uiTB = false;
  1690.     float x;
  1691.     int mX, mY;
  1692.     int left, right, bottom, top;
  1693.     int textLeft, textRight, textBot, textTop;
  1694.  
  1695.     Get2DIntBounds(textBox, &left, &right, &bottom, &top);
  1696.  
  1697.     /* return if mouse outside text box */
  1698.     if (mouseX < left || mouseX > right || mouseY < bottom 
  1699.             || mouseY > top) return ObjFalse;
  1700.  
  1701.     if (TOOL(flags) == T_HELP) /* help mode? */
  1702.     {
  1703.     ContextHelp(textBox);
  1704.     return ObjTrue;
  1705.     }
  1706.     
  1707.     /* get text parameters and scan text */
  1708.  
  1709.     theObj = GetIntVar("PressOneLiner", textBox, STYLE);
  1710.     if (!theObj) style = PLAIN;
  1711.     else style = GetInt(theObj);
  1712.  
  1713.     /* return if not editable */
  1714.     if (!(style & EDITABLE)) return ObjTrue;
  1715.  
  1716.     /* return if not active */
  1717.     if (!GetPredicate(textBox, ACTIVATED)) return ObjTrue;
  1718.  
  1719.     MakeMeCurrent(textBox);
  1720.  
  1721.     MakeVar(textBox, VALUE);
  1722.     theObj = GetVar(textBox, VALUE);
  1723.     if (theObj) text = GetString(theObj);
  1724.     else text = "\0";
  1725.  
  1726.     theObj = GetVar(textBox, ALIGNMENT);
  1727.     if (theObj) align = GetInt(theObj);
  1728.     else align = LEFTALIGN;
  1729.  
  1730.     theObj = GetVar(textBox, UIFONT);
  1731.     if (theObj)
  1732.     {
  1733.     uiFont = GetInt(theObj);
  1734.     size = uiFontInfo[uiFont].size;
  1735.     uiTB = true;
  1736.     }
  1737.     else
  1738.     {
  1739.         theObj = GetStringVar("PressOneLiner", textBox, TEXTFONT);
  1740.         if (theObj) fontName = GetString(theObj);
  1741.         else return ObjFalse;
  1742.  
  1743.         theObj = GetIntVar("PressOneLiner", textBox, TEXTSIZE);
  1744.         if (theObj) size = GetInt(theObj);
  1745.         else return ObjFalse;
  1746.     }
  1747.  
  1748.     theObj = GetVar(textBox, HOROFFSET);
  1749.     if (theObj) horOffset = GetInt(theObj);
  1750.     else horOffset = 0;
  1751.  
  1752.     MakeVar(textBox, BACKGROUND);
  1753.  
  1754.     /* adjust bounds for background or pit */
  1755.     if (style & WITH_PIT)
  1756.     {
  1757.     textLeft = left + TEXTMARGIN;
  1758.     textRight = right - TEXTMARGIN;
  1759.     textBot = bottom + TEXTMARGIN;
  1760.     textTop = top - TEXTMARGIN;
  1761.     }
  1762.     else if (GetVar(textBox, BACKGROUND))
  1763.     {
  1764.     textLeft = left + (HANDLESIZE + 2);
  1765.     textRight = right - (HANDLESIZE + 2);
  1766.     textBot = bottom + (HANDLESIZE + 2);
  1767.     textTop = top - (HANDLESIZE + 2);
  1768.     }
  1769.     else
  1770.     {
  1771.     textLeft = left;
  1772.     textRight = right;
  1773.     textBot = bottom;
  1774.     textTop = top;
  1775.     }
  1776.  
  1777.     if (uiTB) 
  1778.     {
  1779.     SetUIFont(uiFont);
  1780.     }
  1781.     else 
  1782.     {
  1783.     SetupFont(fontName, size);
  1784.     }
  1785.     length = StrWidth(text);
  1786.     
  1787.     /* figure out where line begins (without offset) */
  1788.     switch (align)
  1789.     {
  1790.         case CENTERALIGN:
  1791.         x = (textLeft + textRight)/2 - length/2;
  1792.         break;
  1793.  
  1794.         case RIGHTALIGN:
  1795.         x = textRight - length;
  1796.         break;
  1797.  
  1798.         case LEFTALIGN:
  1799.         default:
  1800.         x = textLeft;
  1801.         break;
  1802.     }
  1803.     for (i=0; text[i]; ++i)
  1804.     {
  1805.     w = ChrWidth(text[i]);
  1806.     offset[i] = x + w/2;
  1807.     x += w;
  1808.     }
  1809.  
  1810.     SetVar(textBox, LASTKEY, NewInt(-1)); /* no previous character */
  1811.     
  1812.     if (flags & F_EXTEND)
  1813.     {
  1814.     i = GetInt(GetVar(textBox, GetPredicate(textBox, FORWARDVECTOR) ? BGNSEL : ENDSEL));
  1815.     }
  1816.     else
  1817.     {
  1818.     /* Find character position of mouse down */
  1819.     for (i=0; text[i] && offset[i] - horOffset < mouseX; ++i) ;
  1820.     }
  1821.     
  1822.     /* track mouse and find char pos of end of selection */
  1823.     mX = mouseX; mY = mouseY; /* setup for at least one loop */
  1824.     cj = -1; /* forces redraw the first time */
  1825.     do {
  1826.     for (j=0; text[j] && offset[j] - horOffset < mX; ++j) ;
  1827.     if (j != cj) /* selection has changed */
  1828.     {
  1829.         if (i > j) /* make sure bgnSel < endSel */
  1830.         {
  1831.             SetVar(textBox, BGNSEL, NewInt(j));
  1832.             SetVar(textBox, ENDSEL, NewInt(i));
  1833.             SetVar(textBox, FORWARDVECTOR, ObjFalse);
  1834.         }
  1835.         else
  1836.         {
  1837.             SetVar(textBox, BGNSEL, NewInt(i));
  1838.             SetVar(textBox, ENDSEL, NewInt(j));
  1839.             SetVar(textBox, FORWARDVECTOR, ObjTrue);
  1840.         }
  1841.         DrawMe(textBox);
  1842.         cj = j;
  1843.         horOffset = GetInt(GetVar(textBox, HOROFFSET));
  1844.     }
  1845.     } while (Mouse(&mX, &mY));
  1846.     
  1847.     if (i == j && flags & F_DOUBLECLICK)
  1848.     {
  1849.     /* select the word at insPt */
  1850.     while (i > 0)
  1851.     {
  1852.         if (IsAlphaNum(text[i-1])) --i;
  1853.         else break;
  1854.     }
  1855.     while (text[j] != '\0')
  1856.     {
  1857.         if (IsAlphaNum(text[j])) ++j;
  1858.         else break;
  1859.     }
  1860.     SetVar(textBox, BGNSEL, NewInt(i));
  1861.     SetVar(textBox, ENDSEL, NewInt(j));
  1862.     }
  1863. #endif
  1864.     return ObjTrue;
  1865. }
  1866.  
  1867. int IsAlphaNum(c)
  1868. char c;
  1869. {
  1870.     return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <='9';
  1871. }
  1872.  
  1873. static ObjPtr EditTextBox(textBox, key, flags)
  1874. ObjPtr textBox;
  1875. int key;
  1876. long flags;
  1877. {
  1878. #ifdef INTERACTIVE
  1879.     ObjPtr theObj, theText, theStyle, theInsPt, theEndSel;
  1880.     char *text;
  1881.     int i, j, len, style, insPt, endSel, lastKey;
  1882.     int left, right, bottom, top;
  1883.  
  1884.     if (!AmICurrent(textBox)) return ObjFalse; /* keyboard input not for me */
  1885.  
  1886.     theStyle = GetIntVar("EditTextBox", textBox, STYLE);
  1887.     if (!theStyle) return ObjTrue;
  1888.     style = GetInt(theStyle);
  1889.     if (!(style & EDITABLE)) return ObjTrue;
  1890.  
  1891. #if 0
  1892.     Not really reliable enough yet
  1893.     /*EMP Set up owner for faster redrawing*/
  1894.     if (style & ADJUSTABLE)
  1895.     {
  1896.     ObjPtr parent;
  1897.     parent = GetVar(textBox, PARENT);
  1898.     if (parent)
  1899.     {
  1900.         if (key)
  1901.         {printf("tempObscured is true\n");
  1902.         SetVar(parent, TEMPOBSCURED, ObjTrue);
  1903.         }
  1904.         else
  1905.         {printf("tempObscured is false\n");
  1906.         SetVar(parent, TEMPOBSCURED, ObjFalse);
  1907.         }
  1908.     }
  1909.     }
  1910. #endif
  1911.     Get2DIntBounds(textBox, &left, &right, &bottom, &top);
  1912.  
  1913.     /* adjust bounds */
  1914.     if (style & WITH_PIT)
  1915.     {
  1916.         left += TEXTMARGIN;
  1917.         right -= TEXTMARGIN;
  1918.         bottom += TEXTMARGIN;
  1919.         top -= TEXTMARGIN;
  1920.     }
  1921.     else if (style & ADJUSTABLE || GetVar(textBox, BACKGROUND))
  1922.     {
  1923.         left += HANDLESIZE + 2;
  1924.         right -= HANDLESIZE + 2;
  1925.         bottom += HANDLESIZE + 2;
  1926.         top -= HANDLESIZE + 2;
  1927.     }
  1928.  
  1929.     theText = GetStringVar("EditTextBox", textBox, VALUE);
  1930.     if (theText) text = GetString(theText);
  1931.     else text = "\0";
  1932.     
  1933.     if ((len = strlen(text)) > textBufSize) /* grow buffer */
  1934.     {
  1935.         textBuf = (char *) Realloc(textBuf, len + TEXTBUFFERINCR);
  1936.         if (!textBuf) /* failed */
  1937.         {
  1938.             OMErr();
  1939.             textBufSize = 0;
  1940.             return NULLOBJ;
  1941.         }
  1942.     }
  1943.  
  1944.     theObj = GetVar(textBox, LASTKEY);
  1945.     if (theObj) lastKey = GetInt(theObj);
  1946.     else lastKey = -1;
  1947.  
  1948.     theInsPt = GetVar(textBox, BGNSEL);
  1949.     if (!theInsPt) insPt = len;
  1950.     else insPt = GetInt(theInsPt);
  1951.  
  1952.     theEndSel = GetVar(textBox, ENDSEL);
  1953.     if (!theEndSel) endSel = len;
  1954.     else endSel = GetInt(theEndSel);
  1955.  
  1956.     /* copy up to the insertion point */
  1957.     for(i=0; i<insPt; ++i) textBuf[i] = text[i];
  1958.         
  1959.     /* process every key press */
  1960.     if (key == '\b' || key == '\177')
  1961.     {
  1962.     if (lastKey < 0 || lastKey != '\b')
  1963.     {
  1964.         SaveForUndo(textBox);
  1965.     }
  1966.     SetVar(textBox, LASTKEY, NewInt('\b'));
  1967.     
  1968.     if (insPt == endSel && insPt > 0) /* delete character before insPt */
  1969.     {
  1970.         for(i = insPt, j = --insPt; i<len; ++i, ++j)
  1971.             textBuf[j] = text[i];
  1972.         textBuf[j] = '\0';
  1973.     }
  1974.     else /* just delete selection */
  1975.     {
  1976.         for(i = endSel, j = insPt; i < len; ++i, ++j)
  1977.             textBuf[j] = text[i];
  1978.         textBuf[j] = '\0';
  1979.     }
  1980.     SetVar(textBox, VALUE, NewString(textBuf));
  1981.     SetVar(textBox, BGNSEL, NewInt(insPt));
  1982.     SetVar(textBox, ENDSEL, NewInt(insPt));
  1983.     SetVar(textBox, CHANGED, ObjTrue);
  1984.     DrawMe(textBox);
  1985.     }
  1986.     else if (key == '\r')
  1987.     {
  1988.         if (style & ONE_LINE) /* check for ENTERMETHOD */
  1989.         {
  1990.             FuncTyp EnterMethod = GetMethod(textBox, ENTERMETHOD);
  1991.             if (EnterMethod) EnterMethod(textBox);
  1992.         }
  1993.         else /* put newline into string at insertion point */
  1994.         {
  1995.             if (lastKey < 0 || lastKey == '\b')
  1996.             {
  1997.             SaveForUndo(textBox);
  1998.             }
  1999.             SetVar(textBox, LASTKEY, NewInt(key));
  2000.  
  2001.             textBuf[insPt] = '\n';
  2002.             for(i=insPt; i<len; ++i) textBuf[i+1] = text[i + endSel - insPt];
  2003.             textBuf[len+1] = '\0';
  2004.             SetVar(textBox, VALUE, NewString(textBuf));
  2005.             SetVar(textBox, BGNSEL, NewInt(insPt + 1));
  2006.             SetVar(textBox, ENDSEL, NewInt(insPt + 1));
  2007.             SetVar(textBox, CHANGED, ObjTrue);
  2008.             DrawMe(textBox);
  2009.         }
  2010.     }
  2011.     else if (key == FK_LEFT_ARROW)
  2012.     {
  2013.     if (flags & F_EXTEND)
  2014.     {
  2015.         if (insPt == endSel) /* no selection */
  2016.         {
  2017.         SetVar(textBox, FORWARDVECTOR, ObjFalse); /* set backward direction */
  2018.         }
  2019.         if (!GetPredicate(textBox, FORWARDVECTOR))
  2020.         {
  2021.         if (insPt > 0) --insPt;
  2022.         }
  2023.         else
  2024.         {
  2025.         if (endSel > 0) --endSel;
  2026.         }
  2027.     }
  2028.     else /* move insPt and endSel back */
  2029.     {
  2030.         if (insPt > 0) --insPt;
  2031.         endSel = insPt;
  2032.     }
  2033.     if (insPt <= endSel)
  2034.     {
  2035.         SetVar(textBox, BGNSEL, NewInt(insPt));
  2036.         SetVar(textBox, ENDSEL, NewInt(endSel));
  2037.     }
  2038.     else
  2039.     {
  2040.         SetVar(textBox, BGNSEL, NewInt(endSel));
  2041.         SetVar(textBox, ENDSEL, NewInt(insPt));
  2042.     }
  2043.     SetVar(textBox, LASTKEY, NewInt(-1));
  2044.     DrawMe(textBox);
  2045.     }
  2046.     else if (key == FK_RIGHT_ARROW)
  2047.     {
  2048.     if (flags & F_EXTEND)
  2049.     {
  2050.         if (insPt == endSel) /* no selection */
  2051.         {
  2052.         SetVar(textBox, FORWARDVECTOR, ObjTrue); /* set forward direction */
  2053.         }
  2054.         if (GetPredicate(textBox, FORWARDVECTOR))
  2055.         {
  2056.         if (endSel < len) ++endSel;
  2057.         }
  2058.         else
  2059.         {
  2060.         if (insPt < len) ++insPt;
  2061.         }
  2062.     }
  2063.     else /*  move endSel and insPt forward */
  2064.     {
  2065.         if (endSel < len) ++endSel;
  2066.         insPt = endSel;
  2067.     }
  2068.     if (insPt <= endSel)
  2069.     {
  2070.         SetVar(textBox, BGNSEL, NewInt(insPt));
  2071.         SetVar(textBox, ENDSEL, NewInt(endSel));
  2072.     }
  2073.     else
  2074.     {
  2075.         SetVar(textBox, BGNSEL, NewInt(endSel));
  2076.         SetVar(textBox, ENDSEL, NewInt(insPt));
  2077.     }
  2078.     SetVar(textBox, LASTKEY, NewInt(-1));
  2079.     DrawMe(textBox);
  2080.     }
  2081.     else if (key == FK_UP_ARROW || key == FK_DOWN_ARROW)
  2082.     {
  2083.     if (style & ONE_LINE)
  2084.     {
  2085.         /* just move to first or last char; scroll if necessary */
  2086.         if (key == FK_UP_ARROW)
  2087.         {
  2088.         if (flags & F_EXTEND)
  2089.         {
  2090.             if (insPt == endSel) /* no selection */
  2091.             SetVar(textBox, FORWARDVECTOR, ObjFalse); /* set backward direction */
  2092.             if (GetPredicate(textBox, FORWARDVECTOR)) endSel = 0;
  2093.             else insPt = 0;
  2094.         }
  2095.         else insPt = endSel = 0;
  2096.         }
  2097.         else
  2098.         {
  2099.         if (flags & F_EXTEND)
  2100.         {
  2101.             if (insPt == endSel) /* no selection */
  2102.             SetVar(textBox, FORWARDVECTOR, ObjTrue); /* set forward direction */
  2103.             if (GetPredicate(textBox, FORWARDVECTOR)) endSel = len;
  2104.             else insPt = len;
  2105.         }
  2106.         else insPt = endSel = len;
  2107.         }
  2108.         if (insPt <= endSel)
  2109.         {
  2110.         SetVar(textBox, BGNSEL, NewInt(insPt));
  2111.         SetVar(textBox, ENDSEL, NewInt(endSel));
  2112.         }
  2113.         else
  2114.         {
  2115.         SetVar(textBox, BGNSEL, NewInt(endSel));
  2116.         SetVar(textBox, ENDSEL, NewInt(insPt));
  2117.         }
  2118.         SetVar(textBox, LASTKEY, NewInt(-1));
  2119.         DrawMe(textBox);
  2120.     }
  2121.     else
  2122.     {
  2123.         /* Oh, so you want to move the cursor up or down, eh? Well,
  2124.            we can do that, it's no bother.  Really.  So what if we have 
  2125.            to read all of the text and create a few arrays?  We don't
  2126.            mind. It's no trouble at all, really.  Have a seat. */
  2127.     
  2128.         int size, align, l, offset[MAXNLINES * MAXLINE], lnx[MAXNLINES + 1];
  2129.         char *t, *fontName, lineBuf[MAXLINE + 1];
  2130.         int uiFont, width, length, tabWid, nTabs;
  2131.         float x;
  2132.         Bool uiTB = false;
  2133.     
  2134.         theObj = GetVar(textBox, ALIGNMENT);
  2135.         if (theObj) align = GetInt(theObj);
  2136.         else align = LEFTALIGN;
  2137.     
  2138.         theObj = GetVar(textBox, UIFONT);
  2139.         if (theObj)
  2140.         {
  2141.         uiFont = GetInt(theObj);
  2142.         size = uiFontInfo[uiFont].size;
  2143.         uiTB = true;
  2144.         }
  2145.         else
  2146.         {
  2147.             theObj = GetStringVar("EditTextBox", textBox, TEXTFONT);
  2148.             if (theObj) fontName = GetString(theObj);
  2149.             else return ObjFalse;
  2150.     
  2151.             theObj = GetIntVar("EditTextBox", textBox, TEXTSIZE);
  2152.             if (theObj) size = GetInt(theObj);
  2153.             else return ObjFalse;
  2154.            }
  2155.  
  2156.         MakeVar(textBox, BACKGROUND);
  2157.     
  2158.         width = right - left;
  2159.  
  2160.         if (uiTB)
  2161.         {
  2162.         SetUIFont(uiFont);
  2163.         }
  2164.         else
  2165.         {
  2166.             SetupFont(fontName, size);
  2167.         }
  2168.         tabWid = TABWID*ChrWidth('0');
  2169.     
  2170.         t = text; l = 0; lnx[0] = 0; indent = 0; /* setup for loop */
  2171.         do {
  2172.             t = NextLine(t, width - indent, lineBuf);
  2173.             lnx[++l] = t - text; /* index to first char of next line */
  2174.             length = StrWidth(lineBuf);
  2175.             
  2176.             /* figure out where line begins */
  2177.             switch (align)
  2178.             {
  2179.             case CENTERALIGN:
  2180.                 x = (left + right)/2 - length/2;
  2181.                 break;
  2182.     
  2183.             case RIGHTALIGN:
  2184.                 x = right - length;
  2185.                 break;
  2186.     
  2187.             case LEFTALIGN:
  2188.             default:
  2189.                 x = left + indent;
  2190.                 break;
  2191.             }
  2192.     
  2193.             /* build next line of char offsets */
  2194.             for (i=lnx[l-1]; i<lnx[l]; ++i)
  2195.             {
  2196.                 register int w;
  2197.                 if (text[i] == '\t')
  2198.                 {
  2199.                     w = x;
  2200.                     x = left + ( indent = (++nTabs)*tabWid );
  2201.                     offset[i] = (x + w)/2;
  2202.                 }
  2203.                 else
  2204.                 {
  2205.                     w = ChrWidth(text[i]);
  2206.                     offset[i] = x + w/2;
  2207.                     x += w;
  2208.                 }
  2209.             }
  2210.             if (*t == '\n')
  2211.             {
  2212.                 ++t; /* paragraph */
  2213.                 indent = 0;
  2214.                 nTabs = 0;
  2215.             }
  2216.             if (l > MAXNLINES) break;
  2217.         } while (*t);
  2218.     
  2219.         /* find the line containing insPt */
  2220.         for (i=1; i<=l; ++i) if (lnx[i] >= insPt) break;
  2221.     
  2222.         /* line is i-1 */
  2223.     
  2224.         if (key == FK_UP_ARROW)
  2225.         {
  2226.             int k;
  2227.     
  2228.             if (i-1 == 0) return ObjTrue; /* already on first line */
  2229.     
  2230.             for (k=lnx[i-2]; k<lnx[i-1]; ++k)
  2231.                 if (offset[k] > offset[insPt]) break;
  2232.             SetVar(textBox, BGNSEL, theObj = NewInt(k));
  2233.             SetVar(textBox, ENDSEL, theObj);
  2234.             SetVar(textBox, LASTKEY, NewInt(-1));
  2235.             DrawMe(textBox);
  2236.         }
  2237.         else if (key == FK_DOWN_ARROW)
  2238.         {
  2239.             int k;
  2240.     
  2241.             if (i == l) return ObjTrue; /* already on last line */
  2242.     
  2243.             for (k=lnx[i]; k<lnx[i+1]; ++k)
  2244.                 if (offset[k] > offset[insPt]) break;
  2245.             SetVar(textBox, BGNSEL, theObj = NewInt(k));
  2246.             SetVar(textBox, ENDSEL, theObj);
  2247.             SetVar(textBox, LASTKEY, NewInt(-1));
  2248.             DrawMe(textBox);
  2249.         }
  2250.     }
  2251.     }
  2252.     else if (key == '\0')
  2253.     {
  2254.         if (logging) LogControl(textBox);
  2255.         ChangedValue(textBox);
  2256.  
  2257.         /*** MakeMeCurrent(NULLOBJ); ***/
  2258.     }
  2259.     else if (key >= ' ' || key == '\t' && !(style & ONE_LINE))
  2260.     {
  2261.     if (lastKey < 0 || lastKey == '\b')
  2262.     {
  2263.         SaveForUndo(textBox);
  2264.     }
  2265.     SetVar(textBox, LASTKEY, NewInt(key));
  2266.  
  2267.     if (style & ONE_LINE && len + 1 - (endSel - insPt) > MAXLINE)
  2268.         return ObjTrue; /* no more text will fit */
  2269.     
  2270.     /* add key at insertion point */
  2271.     textBuf[insPt] = key;
  2272.     for(i = endSel, j = ++insPt; i < len; ++i, ++j)
  2273.         textBuf[j] = text[i];
  2274.     textBuf[j] = '\0';
  2275.     SetVar(textBox, VALUE, NewString(textBuf));
  2276.     SetVar(textBox, BGNSEL, NewInt(insPt));
  2277.     SetVar(textBox, ENDSEL, NewInt(insPt));
  2278.     SetVar(textBox, CHANGED, ObjTrue);
  2279.     DrawMe(textBox);
  2280.     }
  2281. #endif
  2282.     return ObjTrue;
  2283. }
  2284.  
  2285. /************************************************************ CUT, COPY, PASTE*/
  2286. static ObjPtr CutText(textBox)
  2287. ObjPtr textBox;
  2288. {
  2289.     ObjPtr p, retVal;
  2290.     int i, j, len, style, insPt, endSel;
  2291.     char *text;
  2292.     Bool deferChange;
  2293.  
  2294.     if (p = GetStringVar("CutText", textBox, VALUE)) text = GetString(p);
  2295.     else text = "\0";
  2296.     
  2297.     len = strlen(text);
  2298.  
  2299.     if (p = GetVar(textBox, BGNSEL)) insPt = GetInt(p);
  2300.     else insPt = len;
  2301.  
  2302.     if (p = GetVar(textBox, ENDSEL)) endSel = GetInt(p);
  2303.     else endSel = len;
  2304.     
  2305.     if (insPt == endSel) return NewString("\0");
  2306.     
  2307.     SaveForUndo(textBox);
  2308.     
  2309.     style = GetInt(GetVar(textBox, STYLE));
  2310.  
  2311.     /* copy the selection to textBuf */
  2312.     for (i=insPt, j=0; i<endSel; ++i, ++j) textBuf[j] = text[i];
  2313.     textBuf[j] = '\0';
  2314.  
  2315.     /* delete selection */
  2316.     for (j=insPt, i=endSel; text[i]!='\0'; ++i, ++j) text[j] = text[i];
  2317.     text[j] = '\0';
  2318.  
  2319.     SetVar(textBox, VALUE, NewString(text));
  2320.     SetVar(textBox, ENDSEL, NewInt(insPt));
  2321.     SetVar(textBox, CHANGED, ObjTrue);
  2322.     
  2323.     ChangedValue(textBox);
  2324.     if (logging) LogControl(textBox);
  2325.     
  2326.     ImInvalid(textBox);
  2327.     retVal = NewString(textBuf);
  2328.     ToClipboard(retVal);
  2329.     return retVal;
  2330. }
  2331.  
  2332. static ObjPtr CopyText(textBox)
  2333. ObjPtr textBox;
  2334. {
  2335.     ObjPtr p, retVal;
  2336.     int i, j, len, insPt, endSel;
  2337.     char *text;
  2338.  
  2339.     if (p = GetStringVar("CopyText", textBox, VALUE))
  2340.         text = GetString(p);
  2341.     else text = "\0";
  2342.     
  2343.     len = strlen(text);
  2344.  
  2345.     if (p = GetVar(textBox, BGNSEL)) insPt = GetInt(p);
  2346.     else insPt = len;
  2347.  
  2348.     if (p = GetVar(textBox, ENDSEL)) endSel = GetInt(p);
  2349.     else endSel = len;
  2350.  
  2351.     /* copy the selection to textBuf */
  2352.     for (i=insPt, j=0; i<endSel; ++i, ++j) textBuf[j] = text[i];
  2353.  
  2354.     /* terminate and return */
  2355.     textBuf[j] = '\0';
  2356.     retVal = NewString(textBuf);
  2357.  
  2358.     ToClipboard(retVal);
  2359.     return retVal;
  2360. }
  2361.  
  2362. static ObjPtr PasteText(textBox)
  2363. ObjPtr textBox;
  2364. {
  2365.     ObjPtr p;
  2366.     int i, j, len, style, insPt, endSel;
  2367.     char *text, *t;
  2368.     Bool deferChange;
  2369.     ObjPtr newText;
  2370.  
  2371.     newText = FromClipboard();
  2372.  
  2373.     if (!IsString(newText)) return NULLOBJ;
  2374.  
  2375.     t = GetString(newText);
  2376.  
  2377.     if (p = GetStringVar("PasteText", textBox, VALUE))
  2378.     text = GetString(p);
  2379.     else text = "\0";
  2380.     
  2381.     len = strlen(text);
  2382.  
  2383.     if (p = GetVar(textBox, BGNSEL)) insPt = GetInt(p);
  2384.     else insPt = len;
  2385.  
  2386.     if (p = GetVar(textBox, ENDSEL)) endSel = GetInt(p);
  2387.     else endSel = len;
  2388.  
  2389.     if ((len += strlen(t)) > textBufSize) /* grow buffer */
  2390.     {
  2391.     textBuf = (char *) Realloc(textBuf, len + TEXTBUFFERINCR);
  2392.     if (!textBuf) /* failed */
  2393.     {
  2394.         OMErr();
  2395.         textBufSize = 0;
  2396.         return NULLOBJ;
  2397.     }
  2398.     }
  2399.     
  2400.     style = GetInt(GetVar(textBox, STYLE));
  2401.     if (style & ONE_LINE && len > MAXLINE) return NULLOBJ; /* won't fit */
  2402.     
  2403.     SaveForUndo(textBox);
  2404.  
  2405.     /* copy up to the insertion point */
  2406.     for (i=0; i<insPt; ++i) textBuf[i] = text[i];
  2407.  
  2408.     /* copy the paste text */
  2409.     for (j=0; t[j]!='\0'; ++i, ++j) textBuf[i] = t[j];
  2410.  
  2411.     insPt = i; /* remember new insertion point */
  2412.  
  2413.     /* copy text after endSel */
  2414.     for (j=endSel; text[j]!='\0'; ++i, ++j) textBuf[i] = text[j];
  2415.  
  2416.     textBuf[i] = '\0';
  2417.     SetVar(textBox, VALUE, NewString(textBuf));
  2418.     SetVar(textBox, BGNSEL, p = NewInt(insPt));
  2419.     SetVar(textBox, ENDSEL, p);
  2420.     SetVar(textBox, CHANGED, ObjTrue);
  2421.  
  2422.     ChangedValue(textBox);
  2423.     if (logging) LogControl(textBox);
  2424.     
  2425.     ImInvalid(textBox);
  2426.     return NULLOBJ;
  2427. }
  2428.  
  2429. /********************************************************** SELECT ALL */
  2430. ObjPtr SelectAll(textBox)
  2431. ObjPtr textBox;
  2432. {
  2433.     SetVar(textBox, BGNSEL, NewInt(0));
  2434.     MakeVar(textBox, VALUE);
  2435.     SetVar(textBox, ENDSEL, NewInt(strlen(GetString(GetVar(textBox,VALUE)))));
  2436.     MakeMeCurrent(textBox);
  2437.     return ObjTrue;
  2438. }
  2439.  
  2440. static ObjPtr TextBoxNotCurrent(textBox)
  2441. ObjPtr textBox;
  2442. /*Makes a textbox not current*/
  2443. {
  2444.     ImInvalid(textBox);
  2445.     SetVar(textBox, LASTKEY, NewInt(-1));
  2446.     SetVar(textBox, HOROFFSET, NewInt(0));
  2447. }
  2448.  
  2449. /* adapted from ShowPaletteDisplayControls by Eric Pepke */
  2450. static ObjPtr ShowTextBoxControls(display, windowName)
  2451. ObjPtr display;
  2452. char *windowName;
  2453. /* Makes a new control window to control a textbox */
  2454. {
  2455.     WinInfoPtr controlWindow;
  2456.     ObjPtr var;
  2457.     ObjPtr panel;
  2458.     ObjPtr corral;
  2459.     ObjPtr contents;
  2460.     WinInfoPtr dialogExists;
  2461.  
  2462.     dialogExists = DialogExists((WinInfoPtr) display, NewString("Controls"));
  2463.     controlWindow = GetDialog((WinInfoPtr) display, NewString("Controls"), windowName, 
  2464.     TBPALWINWIDTH, TBPALWINHEIGHT, TBPALWINWIDTH,
  2465.     TBPALWINHEIGHT, WINUI + WINFIXEDSIZE);
  2466.     
  2467.     if (!dialogExists)
  2468.     {
  2469.     long info;
  2470.     ObjPtr value;
  2471.     
  2472.     ObjPtr checkBox, icon, name, colorBar, titleBox, textBox, button;
  2473.     ObjPtr colorWheel, slider, radioGroup;
  2474.     int left, right, bottom, top;
  2475.  
  2476.     SetVar((ObjPtr) controlWindow, REPOBJ, display);
  2477.  
  2478.     /*Set help string*/
  2479.     SetVar((ObjPtr) controlWindow, HELPSTRING, NewString("This window \
  2480. shows controls for an annotation.  For information about any of the controls \
  2481. in the window, use Help In Context on the control.\n"));
  2482.  
  2483.     /*Add in a panel*/
  2484.     GetWindowBounds(&left, &right, &bottom, &top);
  2485.     panel = NewPanel(greyPanelClass, 0, right - left, 0, top - bottom);
  2486.     if (!panel)
  2487.     {
  2488.         return NULLOBJ;
  2489.     }
  2490.     contents = GetVar((ObjPtr) controlWindow, CONTENTS);
  2491.     PrefixList(contents, panel);
  2492.     SetVar(panel, PARENT, (ObjPtr) controlWindow);
  2493.  
  2494.     contents = GetVar(panel, CONTENTS);
  2495.  
  2496.     /*Add in the group of controls for text color*/
  2497.     left = MAJORBORDER;
  2498.     top = TBPALWINHEIGHT - MAJORBORDER;
  2499.     right = left + 4 * MAJORBORDER + COLORWHEELWIDTH + SLIDERWIDTH;
  2500.     
  2501.     titleBox = NewTitleBox(left, right,
  2502.             top - TITLEBOXTOP - MAJORBORDER - 2 * MINORBORDER - COLORWHEELWIDTH
  2503.                 - CHECKBOXHEIGHT - TEXTBOXHEIGHT - TEXTBOXSEP,
  2504.             top, "Text");
  2505.     SetVar(titleBox, PARENT, panel);
  2506.     PrefixList(contents, titleBox);
  2507.     left += MAJORBORDER;
  2508.     right -= MAJORBORDER;
  2509.     top -= TITLEBOXTOP + MAJORBORDER;
  2510.  
  2511.     /*Make the color wheel*/
  2512.     colorWheel = NewColorWheel(left, left + COLORWHEELWIDTH,
  2513.             top - COLORWHEELWIDTH, top, "Text Color");
  2514.     SetVar(colorWheel, PARENT, panel);
  2515.     PrefixList(contents, colorWheel);
  2516.     AssocColorControlWithVar(colorWheel, display, COLOR);
  2517.     SetVar(colorWheel, HELPSTRING, NewString("This color wheel controls the \
  2518. hue and saturation of the color used to draw the text in the annotation.  \
  2519. The final color is a combination of this hue and saturation and the value, or brightness, \
  2520. given by the Value slider."));
  2521.     
  2522.     /*Make the text box below*/
  2523.     textBox = NewTextBox(left, left + COLORWHEELWIDTH,
  2524.             top - COLORWHEELWIDTH - TEXTBOXSEP - TEXTBOXHEIGHT,
  2525.             top - COLORWHEELWIDTH - TEXTBOXSEP,
  2526.             PLAIN, "Text Color Label", "Color");
  2527.     SetVar(textBox, PARENT, panel);
  2528.     PrefixList(contents, textBox);
  2529.     SetTextAlign(textBox, CENTERALIGN);
  2530.  
  2531.     /*Make the brightness slider*/
  2532.     slider = NewSlider(right - SLIDERWIDTH, right, 
  2533.                top - COLORWHEELWIDTH, top,
  2534.                PLAIN, "Text Color Value");
  2535.     SetVar(slider, PARENT, panel);
  2536.     PrefixList(contents, slider);
  2537.     SetSliderRange(slider, 1.0, 0.0, 0.0);
  2538.     AssocBrightnessControlWithVar(slider, display, COLOR);
  2539.     SetVar(slider, HELPSTRING, NewString("This slider controls the \
  2540. value, or brightness, of the color used to draw the text in the annotation.  \
  2541. The final color is a combination of this value and the hue and saturation \
  2542. given by the Color color wheel."));
  2543.  
  2544.     /*Make the text box below*/
  2545.     textBox = NewTextBox(right - SLIDERWIDTH - MAJORBORDER, right + MAJORBORDER,
  2546.             top - COLORWHEELWIDTH - TEXTBOXSEP - TEXTBOXHEIGHT,
  2547.             top - COLORWHEELWIDTH - TEXTBOXSEP,
  2548.             PLAIN, "Text Value Label", "Value");
  2549.     SetVar(textBox, PARENT, panel);
  2550.     PrefixList(contents, textBox);
  2551.     SetTextAlign(textBox, CENTERALIGN);
  2552.     
  2553.     /*Make the background controls*/
  2554.     top = TBPALWINHEIGHT - MAJORBORDER;
  2555.     right = TBPALWINWIDTH - MAJORBORDER;
  2556.     left = right - (4 * MAJORBORDER + COLORWHEELWIDTH + SLIDERWIDTH);
  2557.     
  2558.     titleBox = NewTitleBox(left, right,
  2559.             top - TITLEBOXTOP - MAJORBORDER - 2 * MINORBORDER - COLORWHEELWIDTH
  2560.                 - CHECKBOXHEIGHT - TEXTBOXHEIGHT - TEXTBOXSEP,
  2561.             top, "Background");
  2562.     SetVar(titleBox, PARENT, panel);
  2563.     PrefixList(contents, titleBox);
  2564.     left += MAJORBORDER;
  2565.     right -= MAJORBORDER;
  2566.     top -= TITLEBOXTOP + MAJORBORDER;
  2567.  
  2568.     /*Make the color wheel*/
  2569.     colorWheel = NewColorWheel(left, left + COLORWHEELWIDTH,
  2570.             top - COLORWHEELWIDTH, top, "Background Color");
  2571.     SetVar(colorWheel, PARENT, panel);
  2572.     PrefixList(contents, colorWheel);
  2573.     AssocColorControlWithVar(colorWheel, display, BACKGROUND);
  2574.     SetVar(colorWheel, HELPSTRING, NewString("This color wheel controls the \
  2575. hue and saturation of the color used to draw the background of the annotation.  \
  2576. The final color is a combination of this hue and saturation and the value, or brightness, \
  2577. given by the Value slider."));
  2578.     
  2579.     /*Make the text box below*/
  2580.     textBox = NewTextBox(left, left + COLORWHEELWIDTH,
  2581.             top - COLORWHEELWIDTH - TEXTBOXSEP - TEXTBOXHEIGHT,
  2582.             top - COLORWHEELWIDTH - TEXTBOXSEP,
  2583.             PLAIN, "Background Color Label", "Color");
  2584.     SetVar(textBox, PARENT, panel);
  2585.     PrefixList(contents, textBox);
  2586.     SetTextAlign(textBox, CENTERALIGN);
  2587.  
  2588.     /*Make the brightness slider*/
  2589.     slider = NewSlider(right - SLIDERWIDTH, right, 
  2590.                top - COLORWHEELWIDTH, top,
  2591.                PLAIN, "Background Value");
  2592.     SetVar(slider, PARENT, panel);
  2593.     PrefixList(contents, slider);
  2594.     SetSliderRange(slider, 1.0, 0.0, 0.0);
  2595.     AssocBrightnessControlWithVar(slider, display, BACKGROUND);
  2596.     SetVar(slider, HELPSTRING, NewString("This slider controls the \
  2597. value, or brightness, of the color used to draw the background of the annotation.  \
  2598. The final color is a combination of this value and the hue and saturation \
  2599. given by the Color color wheel."));
  2600.  
  2601.     /*Make the text box below*/
  2602.     textBox = NewTextBox(right - SLIDERWIDTH - MAJORBORDER, right + MAJORBORDER,
  2603.             top - COLORWHEELWIDTH - TEXTBOXSEP - TEXTBOXHEIGHT,
  2604.             top - COLORWHEELWIDTH - TEXTBOXSEP,
  2605.             PLAIN, "Background Value Label", "Value");
  2606.     SetVar(textBox, PARENT, panel);
  2607.     PrefixList(contents, textBox);
  2608.     SetTextAlign(textBox, CENTERALIGN);
  2609.  
  2610.     left -= MINORBORDER;    
  2611.     right += MINORBORDER;
  2612.     /*Make the check box*/
  2613.     top -= COLORWHEELWIDTH + TEXTBOXSEP + TEXTBOXHEIGHT + MINORBORDER;
  2614.     checkBox = NewCheckBox(left, right, 
  2615.         top - CHECKBOXHEIGHT, top,
  2616.         "No Background", true);
  2617.     SetVar(checkBox, PARENT, panel);
  2618.     PrefixList(contents, checkBox);
  2619.     AssocInhibitControlWithVar(checkBox, display, BACKGROUND, NewInt(UIBLACK));
  2620.     SetVar(checkBox, HELPSTRING, NewString("This checkbox controls whether \
  2621. a background is shown.  If it is selected, no background is shown, and the \
  2622. objects behind can be seen."));
  2623.  
  2624.     top -= CHECKBOXHEIGHT + MINORBORDER + MINORBORDER;
  2625.     left = MAJORBORDER;
  2626.     right = TBPALWINWIDTH - MAJORBORDER;
  2627.     }
  2628.     return (ObjPtr) controlWindow;
  2629. }
  2630.  
  2631. /************************************************************* LINE COUNT */
  2632. #ifdef PROTO
  2633. int LineCount(char *text, int width)
  2634. #else
  2635. int LineCount(text, width)
  2636. char *text;
  2637. int width;
  2638. #endif
  2639. {
  2640.     char lineBuf[MAXLINE + 1];
  2641.     int lineCount = 0;
  2642.     int tabWid = TABWID*ChrWidth('0');
  2643.  
  2644.     indent = 0;
  2645.     do {
  2646.         text = NextLine(text, width - indent, lineBuf);
  2647.         if (*text == '\n')
  2648.         {
  2649.             ++text;
  2650.             indent = 0;
  2651.         }
  2652.         else indent += ChrCount(lineBuf, '\t')*tabWid;
  2653.         if (*lineBuf == '\0') break; /* no text or width too small */
  2654.         ++lineCount;
  2655.     } while (*text);
  2656.     return lineCount;
  2657. }
  2658.  
  2659. /*************************************************************** NEXTLINE ******/
  2660.  
  2661. /*    This function assembles words into line buffer from text buffer. It
  2662.     returns a pointer to the next word in text or to terminator if done. 
  2663.     A newline indicates end of paragraph and terminates current line. The 
  2664.     return pointer points to the newline. All spaces are copied to the 
  2665.     new line except extra spaces that won't fit at the end of a line.  
  2666.     If a single word is too long for the line, it will be broken
  2667.     after the last letter that fits. If the line buffer is not big enough 
  2668.     to hold the number of characters that will fit in the width given, the 
  2669.     line will be terminated after the last word that fits.  Other than 
  2670.     newlines and soft eol (\r), no control characters should be in text.
  2671.  
  2672.     1/21/92: indentation scheme added. Now the tab character can be included
  2673.     in the text. There are fixed tab stops at multiples of TABWID * width
  2674.     of a digit in the current font. The indentation is a function only of the
  2675.     number of tabs, not the size of the text between tabs; to avoid overlapping
  2676.     text, it may be necessary to use multiple tabs. After the last tab, wrapped 
  2677.     lines will continue to be indented to that tab stop until a newline is 
  2678.     encountered. NOTE: Indentation only applies to left-aligned text.  
  2679. */
  2680.  
  2681. static char *NextLine(text, width, line)
  2682. char *text;     /* pointer into source text buffer */
  2683. int width;    /* width of destination space in pixels */
  2684. char *line;     /* line output buffer MAXLINE+1 chars long */
  2685. {
  2686.     char *curWord;
  2687.     int inSpace, tabWid, nTabs;
  2688.     int totWid, n, m, spWid;
  2689.  
  2690.     spWid = ChrWidth(' ');
  2691.     tabWid = TABWID*ChrWidth('0');
  2692.     curWord = text;    /* point to beginning of current word in input text */
  2693.     totWid = n = m = 0;    /* reset offsets in output line */
  2694.     nTabs = 0;
  2695.     inSpace = true;    /* reading space flag */
  2696.     /* move words until:
  2697.         -fill up line buffer
  2698.         -width of characters in line buffer exceed given width
  2699.         -run out of input text
  2700.         -encounter newline
  2701.     */
  2702.     while (n <= MAXLINE)
  2703.     {
  2704.         if (*text == '\r') /* "soft" EOL -- ignore */
  2705.         {
  2706.             ++text;
  2707.         }
  2708.         else if (*text == ' ')
  2709.         {
  2710.             if (!inSpace)
  2711.             {
  2712.                 /* the previous word fit; update fallback pointer */
  2713.                 m = n;    /* where line will end if next word doesn't fit */
  2714.                 inSpace = true;
  2715.             }
  2716.             curWord = text++;
  2717.             line[n++] = ' ';
  2718.             totWid += spWid;
  2719.         }
  2720.         else if (*text == '\t') /* handle tab */
  2721.         {
  2722.             if (!inSpace)
  2723.             {
  2724.                 m = n;
  2725.                 inSpace = true;
  2726.             }
  2727.             curWord = text++;
  2728.             line[n++] = '\t';
  2729.             totWid = (++nTabs)*tabWid;
  2730.         }
  2731.         else if (*text == '\n')
  2732.         {
  2733.             /* terminate line right here */
  2734.             if (inSpace) line[m] = '\0';
  2735.             else line[n] = '\0';
  2736.             return text;
  2737.         }
  2738.         else if (*text == '\0')
  2739.         {
  2740.             /* terminate line right here */
  2741.             if (inSpace) line[m] = '\0';
  2742.             else line[n] = '\0';
  2743.             return text;
  2744.         }
  2745.         else
  2746.         {
  2747.             /* non-space; move it */
  2748.             totWid += ChrWidth(*text);
  2749.             if (totWid > width) break;
  2750.             if (inSpace)    /* beginning new word */
  2751.             {
  2752.                 curWord = text;
  2753.                 inSpace = false;
  2754.             }
  2755.             line[n++] = *text++;
  2756.         }
  2757.     }
  2758.     if (m == 0 && !inSpace)    /* single word too long for line */
  2759.     {
  2760.         /* look for a good place to break it */
  2761.         char *b;
  2762.  
  2763.         line[n] = '\0';
  2764.         if ((b = strrchr(line, '/')) || (b = strrchr(line, '-')))
  2765.         {
  2766.             *(b + 1) = '\0';
  2767.             return text - (line + n - (b + 1));
  2768.         }
  2769.         else
  2770.         {
  2771.             /* oh, well, this is a good place */
  2772.             return text;
  2773.         }
  2774.     }
  2775.     else
  2776.     {
  2777.         line[m] = '\0';
  2778.         if (inSpace)    /* skip to next word */
  2779.         {
  2780.             while (*curWord==' ') ++curWord;
  2781.         }
  2782.         return curWord;
  2783.     }
  2784. }
  2785.